问题 B: 数独
时间限制: 1 Sec 内存限制: 128 MB
题目描述
这个游戏只有一个规则:
将格子填满使得每一行,每一列,和每一个小的九宫格,恰好包含1-9这9个数字
正是由于规则简单而又变化多端,数独一时间风靡全球。
现在,我们希望你能编写一个程序解决数独问题。
输入
输入数据一共9行,每行有9个字符。
输入数据描述了一个待解决的数独,其中,“?”表示数独中的空缺。
我们的输入数据总保证有唯一解。
输出
输出一共9行,每行9个数字,表示你的答案。
样例输入 Copy
5???7??6
?6???5?4
?834???
???182?4?
??1???9??
?7?369???
???543?
1?5???9?
7??2???1
样例输出 Copy
514927386
967831524
283456179
659182743
321574968
478369215
892615437
135748692
746293851
思路:
数独要求每一行、每一列、每一个3*3方阵内的数字不重复
所以我们把方阵分成9个小的方阵,并依次编号
我们需要判断行和列的重复,定义两个bool类型的二维数组
当这一行或这一列有数字或者填好数字后,标记为true
//例如第一行第三列填入数字2
bool p[ ][ ] , l[ ][ ];
p [1][2] = l[3][2] = true;
分成的9个小方阵,每过3列,方阵的序号+1,每过三行,方阵的序号+3
于是可以推出表达式:小方阵序号 = (行数-1) / 3 * 3+(列数-1) / 3+1
因为3的整数倍数/3 得到的答案会比原小方阵序号大1,所以行列数需要-1
最后dfs查找判断即可
注意数字与字符之间的转换
int sd[11][11];//数独方阵
bool p[11][11],l[11][11],fz[11][11];
//行,列,序号小方阵
void _out()//输出
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
printf("%d",sd[i][j]);
printf("\n");
//每一行输出完成,换行
}
exit(0);
//用return的话不会退出dfs函数,会增加运算量
}
void dfs(int x,int y)//深搜,传参行列
{
if(sd[x][y] != 0)//表示已经标记过,这个位置有数字
{
if(x == 9 && y == 9) _out();
//行和列都已经填好数字,输出即可
else if(y == 9) dfs(x+1,1);
//列数等于9,说明这一行已经填好数字,搜索下一行
else dfs(x,y+1);//搜索下一列
}
else//没有标记过,需要填数字
{
for(int i=1;i<=9;i++)
{
if(!p[x][i] && !l[y][i] && (!fz[(x-1)/3*3+(y-1)/3+1][i]))
//判断行列和小方阵中是否被标记
{
sd[x][y] = i;
p[x][i] = true;
l[y][i] = true;
fz[(x-1)/3*3+(y-1)/3+1][i] = true;
//填上数之后标记一下
if(x == 9 && y == 9) _out();
else if(y == 9) dfs(x+1,1);
else dfs(x,y+1);
sd[x][y] = 0;//恢复标记,回溯
p[x][i] = false;
l[y][i] = false;
fz[(x-1)/3*3+(y-1)/3+1][i] = false;
//恢复标记,回溯
}
}
}
}
int main()
{
for(int i=1;i<=9;i++)//枚举行
{
for(int j=1;j<=9;j++)//枚举列
{
char s; int t; cin >> s;
t = s-48;
if(t <= 9)
{
p[i][t] = true;
l[j][t] = true;
fz[(i-1)/3*3+(j-1)/3+1][t] = true;
//填充的不是?的话,表示原来有数字了,打上标记
sd[i][j] = t;//放到方阵中
}
}
}
dfs(1,1);
return 0;
}