题解 P1784 【数独】
stone_juice石汁 · · 题解
我来凑热闹了OvO
其实我刷完这道题的时候,看到有一些DALAO做的方法差不多哎,但是都不是很详细,一部分人可能看不懂哦..。 所以,我写的以下题解会超多,做好心理准备吧。QWQ
数独规则(懂规则的可以跳过)
按照百度上的说法是这样子的:
数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3×3)内的数字均含1-9,不重复。
如上图
重点部分我都用黑体标出来了。规则是这样的,虽然数独是需要逻辑推算的,但是对于计算机来说,就直接枚举每个格子的可能性就可以了(真暴力...)。那么我们如何通过程序实现呢?
判断重复
数独要求每一行、每一列、每一个3×3方阵内的数字,不重复。
行和列重复判断是相当简单的。我们可以定义两个bool型二维数组,当此行(或列)填充数字时,我们可以直接把这行的这个数字打上true表示有数字了。
//譬如第一行第三列填入数字2
bool p[][],l[][];//p:行,l:列;
p[1][2]=l[3][2]=true;
如果后面再填充数字,就判断此行(或列)是否填过这个数字即可。
重点:判断方阵中数字重复
如果判断方阵中数字重复?怎样用行列来表示是几方阵成了个问题。但是不用怕,我们有van能的数学。
观察下面这个数独:
可以看到,每过3列,方阵的序号+1,每过3行,方阵的序号+3。
于是我们有了这样的表达式:
方阵序号=(行数-1)/3*3+(列数-1)/3+1
//注意!行数列数要-1,因为3的整数倍数/3会比原方阵大1,不能满足上述需求。
如果填充了数字,就用这个表达式把相应方阵的相应数字打上true标记就可以了。
有了上述方法,就可以写个深搜函数来解决问题了!
至于剩下的,代码里批注讲哦!上代码!
//stone_juice P1784 数独
#include <bits/stdc++.h>//华丽的开头~
using namespace std;
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++)
cout<<sd[i][j]<<" ";
cout<<endl;
}
exit(0);//注意,此处要用exit(0)。用return的话不会退出dfs函数,会增加运算量。
}
void dfs(int x,int y)//神奇的深搜~
{
if(sd[x][y]!=0)//如果原来这个位置有数字,跳过。
if(x==9&&y==9)_out();//当行列都为9,填充完成,输出~
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]=l[y][i]=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]=l[y][i]=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++)
{
int t;//定义tmp(防止下面代码太长?)
cin>>t;//炫酷地输入
if(t!=0)
p[i][t]=l[j][t]=fz[(i-1)/3*3+(j-1)/3+1][t]=true;
//填充的不是0的话,表示原来有数字了。打上标记。
sd[i][j]=t;//填充进数独。
}
dfs(1,1);//搜搜搜!
return 0;//完美地结束~
}
虽然我的方法不是最优解,但是看我写的这么认真,各位DALAO给个赞呗!祝愿你们早日AC!!
#include <致管理员:我不小心把图弄挂了...请重新审核一下吧谢谢QWQ>