编程之美有一道关于深度搜索和回溯应用的题目——构造数独:
数独的棋盘是由九九八十一个小方格组成的。玩家在每个小格子中,分别天上1至9的任意一个数字,让整个棋盘每一行,每一列,以及每一个3*3的小矩阵中的数字都不重复。
作者给两种解法:
解法一:
下面的GenerateValidMatrix()函数用经典的深度优先搜索来生成一个可行解。从(0,0)开始,对没有处理过的格子,调用GetValidValueList(coCurrent)来获得当前格子可能的取值选择,并从中取一个为当前格子的取值,接着搜索下一个格子。在搜索uocheng中,若出现某个格子没有可行的值,则回溯,修改前一个格子的取值,直到所有的格子都找到可行的取值为止,这是可行解。
本人下面的代码实现参考了http://blog.csdn.net/linyunzju/article/details/7673959博客,但我发现该博主的代码实现有一处代码顺序的执行错误,那就运行其构造数独的结果来看,发现最后一个格子竟然被赋值为0,还有就是他的代码实现只能固定的构造一个数独,一般是前面的格子从小到大赋值,而不是随机的去构造数独,这样不太符合构造不同的数独,本人对此代码做纠正和改进,有不恰之处,请多多包涵。
代码实现:
#ifndef COOR_H
#define COOR_H
//存储格子的行和列坐标
class Coor
{
public:
Coor(int xvalue=0,int yvalue=0)
{
x=xvalue;
y=yvalue;
}
int x;
int y;
};
#endif
#ifndef CELL_H
#define CELL_H
#include<stdlib.h>
//代表每个格子的详细信息
class Cell
{
public:
bool validList[10];//存储格子赋值情况信息
int validValue;//格子赋值
Cell()
{
memset(validList,true,10*sizeof(validList[0]));
validValue=0;
}
//判断是否还存在有效值
bool isNoValidValue()
{
for(int i=1;i<=10;i++)
{
if(validList[i]==true) return false;
}
return true;
}
};
#endif // CELL_H
#include <iostream>
#include <cstdlib>
#include<Coor.h>
#include<Cell.h>
#define CLEAR(a) memset((a), 0, sizeof(a))
#define SIZE 9
Cell cell[SIZE+1][SIZE+1];//存储每个格子详细信息,索引代表格子的行和列坐标
int value[SIZE+1];//索引代表对应格子的赋值,而该数组值代索引的数字值对当前格子的有效性
using namespace std;
int pickNextVailVale(int x,int y)
{
CLEAR(value);
//遍历该已填的数字
for(int i=1;i<y;i++)
{
value[cell[x][i].validValue]=1;
}
//遍历该列已填的数字
for(int i=1;i<x;i++)
{
value[cell[i][y].validValue]=1;
}
//求出是属于哪一个九空格的第一个格子的行和列坐标
int ninegrid_x=(x-1)/3*3+1;
int ninegrid_y=(y-1)/3*3+1;
// 遍历该九空格已填的数字
for(int i=ninegrid_x;i<ninegrid_x+3;i++)
for(int j=ninegrid_y;j<ninegrid_y+3;j++)
{
value[cell[i][j].validValue]=1;
}
//随机生成数字
int randomValue=(int)rand()%9+1;
while(true)
{
//存储已被用过的数字
cell[x][y].validList[randomValue]=false;
//如果该数字没被用过,则返回该数字
if(value[randomValue]==0)
{
return randomValue;
}
//如果所有数字都被用过,则回溯前一个格子
if(cell[x][y].isNoValidValue())
{
return -1;
}
//寻找下一个数字
randomValue++;
randomValue=randomValue%9+1;
}
}
//寻找下一个格子
void next(Coor &curCoor)
{
curCoor.y++;
if(curCoor.y>9)
{
curCoor.y=1;
curCoor.x++;
}
}
//回溯前一个格子
void pre(Coor &curCoor)
{
curCoor.y--;
if(curCoor.y<1)
{
curCoor.y=9;
curCoor.x--;
}
}
int main()
{
Coor coCurrent(1,1);
while(true)
{
//找不到满足的情况
if(coCurrent.x==0&&coCurrent.y==0)
break;
//附一个有效的数字
cell[coCurrent.x][coCurrent.y].validValue=pickNextVailVale(coCurrent.x,coCurrent.y);
//当当前格子没有找到满足的数字时,回溯前一个格子
if(cell[coCurrent.x][coCurrent.y].validValue==-1)
{
//恢复初始化状态
cell[coCurrent.x][coCurrent.y].validValue=0;
memset(cell[coCurrent.x][coCurrent.y].validList,true,10*sizeof(cell[coCurrent.x][coCurrent.y].validList[0]));
pre(coCurrent);
}
else
{
// 满足成功结果
if (coCurrent.y==SIZE && coCurrent.x==SIZE)
{
for (int i=1; i<=SIZE; i++)
{
for (int j=1; j<=SIZE; j++)
cout << cell[i][j].validValue << " ";
cout << endl;
}
break;
}
// 进一步搜索
next(coCurrent);
}
}
return 0;
}
解法二:
假设把整个棋盘分成9个九空格,按顺序分别命名为B1,B2,B3….B9.先从位于中心的九宫格随机用1到9填满该九宫格后,通过置换行的办法把B4和B6矩阵填好,然后对中央小矩阵的每一列做同样的变换,把B2和B8都解决了,再分别用B4和B6通过同样的一些系列矩阵变换生成B1和B7以及B2和B8,代码实现可以参考http://www.cnblogs.com/xulu/archive/2012/05/09/2491346.html。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/xuguoli_beyondboy/article/details/48382145