题目链接:数独
多组输入,每次给一个完整的9*9的数独,保证有答案,要你输出答案。
这道题的数据特别强,不仅要注意搜索的顺序,防止搜索树分支加大,还要注意常数部分的优化。
预处理出每个下标对应的行号,列号,所属块号;预处理出数字对应的二进制有多少个1,可以用lowbit做;预处理出2的整数次幂对应的幂数是多少。
我们用二进制来表示这一行,这一列,这一个块可以使用的数字,若某个数字可以使用则该位置为1。那么检查数独表中这个位置能填上的数字的二进制就是三个数字相与的结果,我们再使用树状数组的技巧,将最后一位1取出,进行下一步搜索即可。
每次搜索的时候我们从能填数字的所有位置中选择可以填数字最少的位置出发以此展开搜索。
#include<bits/stdc++.h>
using namespace std;
char s[99];
int row[19],cal[19],kuai[19];//二进制压位,某位为1表示该数字还没有被使用;
int pos[519];//2^i对应的i;
int ci[519];数字x对应二进制有多少个1;
int need;//有多少个需要填的位置;
int r[99],c[99],k[99];
inline int getrow(int i){
return i/9+1;
}
inline int getcal(int i){
return i%9+1;
}
inline int getkuai(int r,int c){
return ((r-1)/3)*3+1+(c-1)/3;
}
inline int lowbit(int x){
return x&-x;
}
void init(){
need=0;
for(int i=1;i<=9;++i){
row[i]=cal[i]=kuai[i]=(1<<9)-1;
}
for(int i=0;i<81;++i){
if(s[i]=='.'){
++need;
continue;
}
int x=s[i]-'0'-1;
row[r[i]]^=(1<<x);
cal[c[i]]^=(1<<x);
kuai[k[i]]^=(1<<x);
}
}
inline void go(int r,int c,int k,int v){
row[r]^=(1<<v);
cal[c]^=(1<<v);
kuai[k]^=(1<<v);
}
bool dfs(int g){
if(g==need){
return 1;
}
int i=-1,ans=99;
for(int j=0;j<81;++j)
if(s[j]=='.'){
int x=row[r[j]]&cal[c[j]]&kuai[k[j]];
if(!x) return 0;
if(ans>ci[x]){
i=j;
ans=ci[x];
}
}
int x=row[r[i]]&cal[c[i]]&kuai[k[i]];
while(x){
int z=lowbit(x);
x-=z;
go(r[i],c[i],k[i],pos[z]-1);
s[i]='0'+pos[z];
if(dfs(g+1)) return 1;
s[i]='.';
go(r[i],c[i],k[i],pos[z]-1);
}
return 0;
}
int main(){
for(int i=0;i<=8;++i) pos[1<<i]=i+1;
for(int i=1;i<(1<<9);++i)
for(int j=i;j;j-=lowbit(j)) ++ci[i];
for(int i=0;i<81;++i){
r[i]=getrow(i);
c[i]=getcal(i);
k[i]=getkuai(r[i],c[i]);
}
while(scanf("%s",s)!=EOF){
if(s[0]=='e') break;
init();
dfs(0);
printf("%s\n",s);
}
return 0;
}