数独游戏(sudoku)算法 回溯+剪枝
2011-05-08 16:09
453 查看
具体数独游戏是什么,我就不介绍了,好像多余了,你能来看这篇文章,说明你对数独游戏已经有了相当的了解了!还是入正题吧,今天先讲解和发下用回溯+剪枝 求数独游戏,我也看了些回溯+剪枝求数独的算法,很恼火,既没注释,而且算法没有通用性。
今天我给大家讲的回溯+剪枝法,不仅可以用于解决数独问题,而且还可以方便用于其他难题。
先介绍回溯+剪枝法吧:回溯+剪枝
可以这样说 回溯+剪枝 法是解决一般性难题的很好的方法,当然如果难题的规模太大的话,计算机内存和运行时间,可能就超出你的范围了。
回溯法就是在问题的解空间中搜索满足条件的解。
要使用回溯法:需理解三个问题(我的理解)
1、怎样构造解空间
2、在什么条件下回,即剪枝的条件
3、回到哪个节点
在类CGrid中定义静态函数如下
static BOOL IsSingleInRow(int num,int row,int grid[9][9]);
//判断数num 在行row 中是否唯一
static BOOL IsSingleInColumn(int num, int colunm, int grid[9][9]);
//判断数num 在列column 中是否唯一
static BOOL IsSingleGongge(int num,int row,int column,int grid[9][9]);
//判断数num 在行row ,列column所在的子宫格中是否唯一
上面的几个函数,非常非常的简单,即不写了,但在函数GetAllResult中 会被用到
下面主要看GetAllResult是怎么写的哦,这个函数是要求出所有的解哦,并存在filename指定的文件中
static BOOL GetAllResult(int grid[][9], CString filename);//filename 用于存储所有的结果
对了,还有个结构体,非常的重要,用于存储空格的相关信息
struct Blank
{
int row; //空格所在的行
int column; //空格所在的列
int candidate_num; //空格中候选数的个数
int startindex; //标示下次应选择哪个候选数
int candidate[9]; //记录下候选数
};
BOOL CGrid::GetAllResult(int grid[][9], CString filename)
{
int i,j,k;
int blank_num=0; //空格的个数
int candidate_num; //空格中的候选数 个数
int result_num=0; //解的个数
int tryGrid[9][9]j;
struct Blank blank[81]; //空格结构体,最大为81个空格
//求出所有的空格,并同时初始的布局赋值给tryGrid
for (i=0;i<9;i++)
{
for (j=0;j<9;j++)
{
tryGrid[i][j]=grid[i][j];
if (grid[i][j] == 0) //表示空格
{
blank[blank_num].row=i; //记录行号
blank[blank_num].column=j; //记录列号
blank[blank_num].startindex=0;
//标示:下次应选择第几个候选数,初始化为0,第一次选择0这个位置
candidate_num=0;
for (k=1;k<=9;k++)
{
if ( CGrid::IsSingleInRow(k,i,grid)
&& CGrid::IsSingleInColumn(k,j,grid)
&& CGrid::IsSingleGongge(k,i,j,grid))
{
blank[blank_num].candidate[candidate_num++] = k;
}
}
blank[blank_num].candidate_num = candidate_num; //候选数个数
blank_num++; //空格数加1
}
}
}
int tempnum;
int succeed=0; //标示这次选择是否选出了合法的候选数
int node_index=0; //表示节点
int new_node_index=-1;
while (TRUE)
{
succeed=0; //每次选的时候初始化为0
while ( blank[node_index].startindex < blank[node_index].candidate_num)
{
tempnum=blank[node_index].candidate[blank[node_index].startindex];
//从空格i中取的第blank[i].startindex个候选数,
//看是否与已经填的方格冲突,且blank[i].startindex<blank[i].candidate_num
if (CGrid::IsSingleInRow(tempnum,blank[node_index].row,tryGrid)
&& CGrid::IsSingleInColumn(tempnum,blank[node_index].column,tryGrid)
&& CGrid::IsSingleGongge(tempnum,blank[node_index].row,blank[node_index].column,tryGrid))
{
tryGrid[blank[node_index].row][blank[node_index].column] = tempnum; //满足3个条件,填入方格的空格中
succeed = 1;
blank[node_index].startindex++;
node_index++; //选择下一个节点中的候选数
break;
}
else
{
blank[node_index].startindex++;
}
}
//succeed==0 标示这次没选成功,一个都不合法,就回啊
//如果我们发现方格i中的所有候选数均不能满足条件,说明以前的选择有误,
//应重新选择以前空格中的候选数,
//空格下面的p满足condition_12:1、空格还有候选数可选;2、该空格距离当前的空格最近
// cout<<node_index-1<<". "<<blank[node_index-1].startindex-1<<" | ";
if (succeed==0) //succeed==0 标示这次没选成功,一个候选数都不合法
{
//找出满足condition_12 空格p
new_node_index=-1;
for (int p=node_index;p>=0;p--)
{
if ( blank[p].startindex < blank[p].candidate_num )
{
new_node_index=p;
break;
}
}
if (new_node_index==-1) //p==-1表明所有空格均不满足condition_12
{
// AfxMessageBox("无解");
//把结果输出到文件
if (result_num==0)
//cout<<endl<<"no result"<<endl;
CGrid::OutputToFileNoResult(filename);
//这个函数的作用是输出结果,可以自己写,或者你可以输出到控制台上
else
cout<<endl<<result_num<<endl;
return FALSE;
}
//撤销操作
for (int q=new_node_index+1;q <=node_index;q++)
{
tryGrid[blank[q].row][blank[q].column]=0;
blank[q].startindex=0;
}
node_index=new_node_index; //回到p处 重新选择下一个候选数
}
//succeed==1,表示再根节点处找到了合法的候选数
//node_index==blank_num-1 表示已经搜索跟节点,
if (succeed==1 && node_index==blank_num)
{
result_num++;
CGrid::DIsplay(tryGrid);//显示找到的解
//这个函数的作用是输出结果,可以自己写
// 继续找下一个解
new_node_index=-1;
for (int p=node_index-2;p>=0;p--)
{
if ( blank[p].startindex < blank[p].candidate_num )
{
new_node_index=p;
break;
}
}
if (new_node_index==-1) //p==-1表明所有空格均不满足condition_12
{
AfxMessageBox("无更多解");
return FALSE;
}
//撤销操作
for (int q=new_node_index+1;q<node_index;q++)
{
tryGrid[blank[q].row][blank[q].column]=0;
blank[q].startindex=0;
}
node_index = new_node_index; //回到p处 重新选择下一个候选数
}
}
return TRUE;
}
自我评价:程序可读性不好!程序的性能可能不是很好!欢迎拍砖。
第一次在CSDN写文章,写的不好!
今天我给大家讲的回溯+剪枝法,不仅可以用于解决数独问题,而且还可以方便用于其他难题。
先介绍回溯+剪枝法吧:回溯+剪枝
可以这样说 回溯+剪枝 法是解决一般性难题的很好的方法,当然如果难题的规模太大的话,计算机内存和运行时间,可能就超出你的范围了。
回溯法就是在问题的解空间中搜索满足条件的解。
要使用回溯法:需理解三个问题(我的理解)
1、怎样构造解空间
2、在什么条件下回,即剪枝的条件
3、回到哪个节点
在类CGrid中定义静态函数如下
static BOOL IsSingleInRow(int num,int row,int grid[9][9]);
//判断数num 在行row 中是否唯一
static BOOL IsSingleInColumn(int num, int colunm, int grid[9][9]);
//判断数num 在列column 中是否唯一
static BOOL IsSingleGongge(int num,int row,int column,int grid[9][9]);
//判断数num 在行row ,列column所在的子宫格中是否唯一
上面的几个函数,非常非常的简单,即不写了,但在函数GetAllResult中 会被用到
下面主要看GetAllResult是怎么写的哦,这个函数是要求出所有的解哦,并存在filename指定的文件中
static BOOL GetAllResult(int grid[][9], CString filename);//filename 用于存储所有的结果
对了,还有个结构体,非常的重要,用于存储空格的相关信息
struct Blank
{
int row; //空格所在的行
int column; //空格所在的列
int candidate_num; //空格中候选数的个数
int startindex; //标示下次应选择哪个候选数
int candidate[9]; //记录下候选数
};
BOOL CGrid::GetAllResult(int grid[][9], CString filename)
{
int i,j,k;
int blank_num=0; //空格的个数
int candidate_num; //空格中的候选数 个数
int result_num=0; //解的个数
int tryGrid[9][9]j;
struct Blank blank[81]; //空格结构体,最大为81个空格
//求出所有的空格,并同时初始的布局赋值给tryGrid
for (i=0;i<9;i++)
{
for (j=0;j<9;j++)
{
tryGrid[i][j]=grid[i][j];
if (grid[i][j] == 0) //表示空格
{
blank[blank_num].row=i; //记录行号
blank[blank_num].column=j; //记录列号
blank[blank_num].startindex=0;
//标示:下次应选择第几个候选数,初始化为0,第一次选择0这个位置
candidate_num=0;
for (k=1;k<=9;k++)
{
if ( CGrid::IsSingleInRow(k,i,grid)
&& CGrid::IsSingleInColumn(k,j,grid)
&& CGrid::IsSingleGongge(k,i,j,grid))
{
blank[blank_num].candidate[candidate_num++] = k;
}
}
blank[blank_num].candidate_num = candidate_num; //候选数个数
blank_num++; //空格数加1
}
}
}
int tempnum;
int succeed=0; //标示这次选择是否选出了合法的候选数
int node_index=0; //表示节点
int new_node_index=-1;
while (TRUE)
{
succeed=0; //每次选的时候初始化为0
while ( blank[node_index].startindex < blank[node_index].candidate_num)
{
tempnum=blank[node_index].candidate[blank[node_index].startindex];
//从空格i中取的第blank[i].startindex个候选数,
//看是否与已经填的方格冲突,且blank[i].startindex<blank[i].candidate_num
if (CGrid::IsSingleInRow(tempnum,blank[node_index].row,tryGrid)
&& CGrid::IsSingleInColumn(tempnum,blank[node_index].column,tryGrid)
&& CGrid::IsSingleGongge(tempnum,blank[node_index].row,blank[node_index].column,tryGrid))
{
tryGrid[blank[node_index].row][blank[node_index].column] = tempnum; //满足3个条件,填入方格的空格中
succeed = 1;
blank[node_index].startindex++;
node_index++; //选择下一个节点中的候选数
break;
}
else
{
blank[node_index].startindex++;
}
}
//succeed==0 标示这次没选成功,一个都不合法,就回啊
//如果我们发现方格i中的所有候选数均不能满足条件,说明以前的选择有误,
//应重新选择以前空格中的候选数,
//空格下面的p满足condition_12:1、空格还有候选数可选;2、该空格距离当前的空格最近
// cout<<node_index-1<<". "<<blank[node_index-1].startindex-1<<" | ";
if (succeed==0) //succeed==0 标示这次没选成功,一个候选数都不合法
{
//找出满足condition_12 空格p
new_node_index=-1;
for (int p=node_index;p>=0;p--)
{
if ( blank[p].startindex < blank[p].candidate_num )
{
new_node_index=p;
break;
}
}
if (new_node_index==-1) //p==-1表明所有空格均不满足condition_12
{
// AfxMessageBox("无解");
//把结果输出到文件
if (result_num==0)
//cout<<endl<<"no result"<<endl;
CGrid::OutputToFileNoResult(filename);
//这个函数的作用是输出结果,可以自己写,或者你可以输出到控制台上
else
cout<<endl<<result_num<<endl;
return FALSE;
}
//撤销操作
for (int q=new_node_index+1;q <=node_index;q++)
{
tryGrid[blank[q].row][blank[q].column]=0;
blank[q].startindex=0;
}
node_index=new_node_index; //回到p处 重新选择下一个候选数
}
//succeed==1,表示再根节点处找到了合法的候选数
//node_index==blank_num-1 表示已经搜索跟节点,
if (succeed==1 && node_index==blank_num)
{
result_num++;
CGrid::DIsplay(tryGrid);//显示找到的解
//这个函数的作用是输出结果,可以自己写
// 继续找下一个解
new_node_index=-1;
for (int p=node_index-2;p>=0;p--)
{
if ( blank[p].startindex < blank[p].candidate_num )
{
new_node_index=p;
break;
}
}
if (new_node_index==-1) //p==-1表明所有空格均不满足condition_12
{
AfxMessageBox("无更多解");
return FALSE;
}
//撤销操作
for (int q=new_node_index+1;q<node_index;q++)
{
tryGrid[blank[q].row][blank[q].column]=0;
blank[q].startindex=0;
}
node_index = new_node_index; //回到p处 重新选择下一个候选数
}
}
return TRUE;
}
自我评价:程序可读性不好!程序的性能可能不是很好!欢迎拍砖。
第一次在CSDN写文章,写的不好!
相关文章推荐
- [html5游戏开发]数独游戏-完整算法
- 回溯算法--三连游戏(人机对战)
- HDU 1426 Sudoku Killer (回溯 + 剪枝)
- 数独破解(回溯,暴力dfs+剪枝)
- 回溯法----- 解数独游戏(1)
- DLX算法解数独游戏 Java版
- hdu1426 Sudoku Killer(数独游戏)--DFS
- dancing links 算法学习小记 Poj 3074 Sudoku (数独)
- QT编写的数独游戏及一个准确高效生成算法
- [算法专题] 深度优先搜索&回溯剪枝
- 数独(sudoku)游戏的程序求解
- 数独游戏(Sudoku Game)
- 算法之6-回溯法解数独问题
- Java回溯算法解数独问题
- QT编写的数独游戏及一个准确高效生成算法
- [算法专题] 深度优先搜索&回溯剪枝
- 数独(sudoku)游戏的程序求解
- 数独游戏(leetcode) dfs 回溯
- 数独的求解算法 回溯
- 数独游戏的生成算法