数独描述:在9*9的格子内,填入1~9,要求每一行每一列不能有重复数,并且,每个格子所处的3*3格子内不得有重复数。
分析:这是一个典型的递归回溯算法,本例中用一个except[9][9][9]的数组来构造每个格子的禁忌表(就是该格子中不能填入的数)。用变量hs来表示此次填数是向前递归运算还是回溯运算。如果是向前递归,则计算该格子的禁忌表,如果是回溯,则将当前格子中的数加入禁忌表。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
//用于标记是否回溯
int hs=0;
//记录每一个格子的禁忌数字,即当前格子不能填入的数字
int except[9][9][9];
//保存结果
int res[9][9];
//随机获取一个值
int getRan()
{
return rand()/(RAND_MAX+1.0)*9+1;
}
//判断随机的值是否在禁忌表
int inforb(int jg,int *fibd)
{
if(fibd[jg-1]==jg)return 1;
return 0;
}
//判断禁忌表是否已满,如果满则需要回溯
int is_full(int *fbid)
{
int i;
for(i=0;i<9;i++)
{
if(fbid[i]==0)return 0;
}
return 1;
}
//构造数独
void gouzao(int i,int j)
{
if(j==9)
{
i++;
j=0;
}
//当到第10行的时候递归结束
if(i==9)return;
//如果是回溯到当前位置的,则把该值填入禁忌表
if(hs)
{
except[i][j][res[i][j]-1]=res[i][j];
hs=0;
}
else
{
int k,m;
//获取禁忌表
bzero(except[i][j],sizeof(except[i][j]));
for(k=0;k<j;k++)
except[i][j][res[i][k]-1]=res[i][k];
for(k=0;k<i;k++)
except[i][j][res[k][j]-1]=res[k][j];
for(k=3*(i/3);k<3*(i/3+1);k++)
{
int end=0;
for(m=3*(j/3);m<3*(j/3+1);m++)
{
if(k==i&&m==j)
{
end=1;
break;
}
except[i][j][res[k][m]-1]=res[k][m];
}
if(end)break;
}
}
//禁忌表满,回溯
if(is_full(except[i][j]))
{
hs=1;
//由于要回溯了,当前的值清零
res[i][j]=0;
int hsi=i,hsj=j-1;
if(hsj<0)
{
hsj=8;
hsi--;
}
gouzao(hsi,hsj);
}
//禁忌表未满,随机获取值,并记录
else{
int ran;
repeat:
ran=getRan();
if(inforb(ran,except[i][j]))
goto repeat;
res[i][j]=ran;
gouzao(i,j+1);
}
}
int main()
{
srand(time(NULL));
gouzao(0,0);
int i,j;
for(i=0;i<9;i++)
{
for(j=0;j<9;j++)
printf("%d ",res[i][j]);
putchar('\n');
}
}