本文参考http://blog.csdn.net/hustspy1990/article/details/7464698
问题:
构造一个9*9的方格矩阵,玩家要在每个方格中,分别填上1至9的任意一个数字,
让整个棋盘每一列、每一行以及每一个3*3的小矩阵中的数字都不重复。
方法一:回溯法
第一种方法使用回溯法,原理是构建一棵搜索树(深度为9*9),然后对每一个格子用不同的数字进行尝试,如果不与数独构造法则冲突则继续搜索,否则返回到上一个节点换个数继续搜索。这个方法的缺点很明显,在搜索过程中,对于每一个数都要考察其是否满足数独规则,即是否与所在小矩阵中的数字重复和是否存在行列重复。此外如果运气不好的话搜索过程可能会很漫长。好处是生成的数独足够“随机”,除了数独规则外没有什么规律可循,所以如果游戏的留白比较多的话将是一个很考验智商的题目。同时我们似乎可以用这种方法来得到所有合法的数独。
本实现中在搜索过程中使用自加作为选择下一个候选数据的方法。
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
//下面的代码是构造数独的代码
//解法一:回溯法--在当前位置选出备选数字,然后看下一次迭代是否能找到数字满足数独规则,假设在下一次迭代数字累加到
//10以后都未能找到满足条件的数字,则将这一次迭代结果清空并返回上一次迭代的结果+1
//行匹配、列匹配、九宫格匹配
bool isSuDoMatch(int **SuDoKu , int i , int j)
{
int temp = SuDoKu[i][j];
for(int x=0 ; x<9 ; x++)
if(x!=i && SuDoKu[x][j]==temp)
return false;
for(int y=0 ; y<9 ; y++)
if(y!=j && SuDoKu[i][y]==temp)
return false;
int JGG_m = i/3;//(i,j)所属九宫格起始坐标
int JGG_n = j/3;
for(int m=JGG_m*3; m<JGG_m*3+3 ; m++)
for(int n=JGG_n*3; n<JGG_n*3+3 ; n++)
if(m!=i && n!=j && SuDoKu[m][n]==temp)
return false;
return true;
}
void printSuDoKu(int **SuDoKu)
{
printf("创建的数独如下:\n");
for(int i=0 ; i<9 ; i++)
{
for(int j=0 ; j<9 ; j++){
printf("%4d" , SuDoKu[i][j]);
}
printf("\n");
}
}
void createSuDoKu(int **SuDoKu)
{
for(int i=0 ; i<9 ; i++)
for(int j=0 ; j<9 ; j++)
SuDoKu[i][j]=0;
//------添加这部分代码使数独更有随机性,不加这部分也可以
srand(time(0));//设定随机数种子为当前时间,使每次产生随机数不全相同
int k=0;
for(int i=0 ; i<9 ; i++){
k = rand()%81;
SuDoKu[k/9][k%9] = i+1;
}
//-------------------
int i=0;
int j=0;
int cordinate=0;
while(true)
{
i = cordinate/9;
j = cordinate%9;
while(true){
SuDoKu[i][j]++;
if(SuDoKu[i][j] == 10){//回溯
SuDoKu[i][j] = 0;
cordinate--;
break;
}
else if(isSuDoMatch(SuDoKu , i , j)){//这个数字有效
cordinate++;
break;
}
}
if(cordinate == 81){//整个9*9矩阵全部填充完毕
printSuDoKu(SuDoKu);
break;
}
}
}
int main()
{
int **SuDoKu = new int* [9];
for(int i=0 ; i<9 ; i++){
SuDoKu[i] = new int [9];
}
// int SuDoKu[9][9]={0};
createSuDoKu(SuDoKu);
return 0;
}
方法二:置换矩阵法
方法一的解法虽然经典,但并不是唯一的正解。
假设已经有一个 3X3 的矩阵是排列好的, 具体数字先用字母代替. 将小矩阵放到数独正中间;
通过置换行, 填充数独中间三行;
通过置换列, 填充数独中间三列;
还剩下 4 个小方块, 由相邻的矩阵变换可得;
下面提供的方案我暂时没弄明白这个转移矩阵从何而来,留待日后在看哈···
const int transform[3][3] = {{1,2,0}, {0,1,2}, {2,0,1}};//都行,暂时没弄明白这东西怎么弄出来的
//const int transform[3][3] = {{0,1,2},{2,0,1},{1,2,0}};
void GenerateGrid(int* grid);
void Show(int (*grid)[3]);
int main(int argc, char *argv[])
{
int grid[3][3];
GenerateGrid(grid[0]);
Show(grid);
return 0;
}
//随机生成3*3的九宫格
void GenerateGrid(int* grid)
{
int index = 0;
int sum = 36; //0~8之和
memset(grid, 0, sizeof(int) * 9);
srand((unsigned)time(0));
for(int i = 0; i < 8; ++i)
{
index = rand() % 9;//0-8
while(grid[index] != 0)
{
index = rand() % 9;
}
grid[index] = i + 1;//将1-8数字随机的分配在不同的网格中
sum -= index;//定位最后一个未填入数字的下标
}
grid[sum] = 9;
}
//输出已完成的数独
void Show(int (*grid)[3])
{
for(int i = 0; i < 9; ++i)
{
for(int j = 0; j < 9; ++j)
{
//printf("%2d",grid[transform[j/3][i%3]][transform[i/3][j%3]]);//二者都行,没明白这东西怎么弄出来的
printf("%2d",grid[transform[i/3][j%3]][transform[j/3][i%3]]);
}
puts(""); //换行
}
}