上机题目中有一道题目的要求是通过编程实现数独的求解,然后想起了初三去后来所在的高中面试提前录取的时候里面的压轴题便是数独,当时做了好长时间也没搞出来,我还记得监考老师当时和我说:“做不出来就别勉强,先做其他题”,然后就吊死了,最后八道题只对了五道,不过运气还不错,提前录取还是通过了。
不小心说了一些题外话,下面步入正题:
和之前的题目一样,由于评判系统暂未开放,所以我实现后直接放洛谷里面评分了,三个测试点,一个TLE,两个AC,TLE的测试点是题目中给的输入输出样例,运行结果是没问题的,可能还需要进一步的优化。
编程中两种不同的思路
第一种是联想到之前做的一道洛谷题里面的一个数学方法:当两个只含正整数的集合的所有元素的和与积分别相等时,这两个集合相同(这个证明起来比较容易,就不在细说了),然后我的想法就是可以把这个思想用在判断行和列的元素判重的过程里,因为每一行\列都是1~9的一个排列,再对未知元素调用c++生成排列的函数进行穷举(因为算法标签是DFS嘛,所以穷举也不一定TLE),但是仔细想一想,这个要对9行9列都进行判断,此外还要想办法解决9个九宫格的判重问题,所以运行时间可能会很长,于是便放弃这种想法,改用深搜。
进行深搜的对象就是初始二维数组里面为0的元素,然后需要通过判断寻找该位置可以填的数字有哪些,行和列的判重用一个简单的遍历就可以了,然后考虑对于第i行第j列的元素,如果填入数字num的话符不符合条件
int k,x,y,l;
if(i%3==0) y=i/3;//得出竖着第几个方块
else y=i/3+1;
if(j%3==0) x=j/3;//得出横着第几个方块
else x=j/3+1;
先通过简单处理判断出该元素所处的小九宫格的位置,如果有重复的话,返回false
for(k=(y-1)*3+1;k<=y*3;k++)//判断该方块中是否有重复的
{
for(l=(x-1)*3+1;l<=x*3;l++)
{//如果所填数字相同且不与该元素角标重合,则存在重复
if(a[k][l]==num&&k!=i&&l!=j)
return false;
}
}
判重工作做完以后基本就是套用DFS的框架了,大概了解dfs的话,应该就没什么问题了,直接附代码了:
#include<bits/stdc++.h>
using namespace std;
int flag,a[10][10];
bool judge(int i,int j,int num)
{
int k,x,y,l;
if(i%3==0) y=i/3;//得出竖着第几个方块
else y=i/3+1;
if(j%3==0) x=j/3;//得出横着第几个方块
else x=j/3+1;
for(k=1;k<=9;k++)//判断一行是否有重复的
{
if(a[i][k]==num&&k!=j)
return false;
}
for(k=1;k<=9;k++) //判断一列是否有重复的
{
if(a[k][j]==num&&k!=i)
return false;
}
for(k=(y-1)*3+1;k<=y*3;k++)//判断该方块中是否有重复的
{
for(l=(x-1)*3+1;l<=x*3;l++)
{
if(a[k][l]==num&&k!=i&&l!=j)
return false;
}
}
return true;
}
void dfs(int x,int y)
{
//加上flag,表示其输出一个符合条件的答案就退出
if(flag) return;
int i;
if(x==10)//满足条件输出
{
flag = 1;
for(i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
return;
}
if(a[x][y]==0)//判断是否有数
{
for(i=1;i<=9;i++)
{
if(judge(x,y,i))
{
a[x][y]=i;
dfs(x+(y+1)/10,(y+1)%10);//往后一个数,到头换行
}
}
a[x][y]=0;//回溯
}
else
dfs(x+(y+1)/10,(y+1)%10);//有数则搜索下一个
return;
}
int main()
{
int i,j;
for(i=1;i<10;i++)
{
for(j=1;j<10;j++)
{
cin >> a[i][j];
}
}
dfs(1,1);
return 0;
}
可能dfs掌握的还不太好,所以还需要优化(看了一下洛谷的提交记录,最快的有13ms的,上机的那个平台我记得是没有时间限制的,苦于完成深度学习作业的我暂时就不优化了),嗐,菜鸡的自我提升之路任重而道远…