链接:https://www.acwing.com/problem/content/168/
来源:《算法竞赛进阶指南》
数独是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得图中每行、每列、每个 3×3 的九宫格内数字
1∼9 均恰好出现一次。
请编写一个程序填写数独。
输入格式
输入包含多组测试用例。
每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。
每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。
您可以假设输入中的每个谜题都只有一个解决方案。
文件结尾处为包含单词 end 的单行,表示输入结束。
输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。
输入样例:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
输出样例:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936
思路:
在每个空点的时候,遍历所在行,所在列,所在方块,选出能填的数,依次填入,进入下个点,再还原。
毫无疑问,这样会超时。
优化:
用2进制来表示9个数中那个数没被使用,开两个数组,ones和map,ones记录剩余可选数的个数,map记录可以填的数值。就不用等使用的时候一次次的遍历了。另外就是从可能性最少的空点处填起,这样整体递归的规模会小不少,能节约不少时间。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 9;
char str[100];
int col[N],row[N],cell[3][3];//存放不同行,列,区块可以填的数的情况。
int ones[1<<N],map[1<<N];
inline int lowbit(int x)//方便对2进制进行操作
{
return x&-x;
}
void init()//初始化函数
{
for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
cell[i][j]=(1<<N)-1;
}
inline int get(int i,int j)//行列块中能使用的数的交集
{
return col[i]&row[j]&cell[i/3][j/3];
}
bool dfs(int cnt)//cnt为空点数量
{
if(!cnt) return true;//cnt=0时,退出递归
int min=10,x,y;//寻找可以选的数最少的空
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{if(str[9*i+j]=='.')
{int t=ones[get(i,j)];
if(t<min)
{
x=i;
y=j;
min=t;
}
}
}
for(int k=get(x,y);k;k-=lowbit(k))
{
int t=map[lowbit(k)];
col[x]-=1<<t;
row[y]-=1<<t;
cell[x/3][y/3]-=1<<t;
str[x*9+y]='1'+t;
if(dfs(cnt-1)) return true;
col[x]+=1<<t;
row[y]+=1<<t;
cell[x/3][y/3]+=1<<t;
str[x*9+y]='.';
}
return false;
}
int main()
{
for(int i=0;i<N;i++) map[1<<i]=i;//初始化map和ones
for(int i=0;i<1<<N;i++)
{
int s=0;
for(int j=i;j;j-=lowbit(j)) s++;
ones[i]=s;
}
while(cin>>str,str[0]!='e')
{init();
int cnt=0;
for(int i=0,k=0;i<N;i++)
for(int j=0;j<N;j++,k++)
{
if(str[k]!='.')//根据已填入的数,变更行列块的信息
{
int t=str[k]-'1';
col[i]-=1<<t;
row[j]-=1<<t;
cell[i/3][j/3]-=1<<t;
}
else cnt++;
}
dfs(cnt);
cout<<str<<endl;
}
}
总体来说,这题需要优化,并且优化的方式比较复杂。很巧妙,我自己肯定很难想到。需要把这段代码多敲几次,多多感受优化的思想。