题目:POJ3076.
题目大意:给定一个
16
∗
16
16*16
16∗16的数独,要求填完这个数独.
首先考虑给每一个空格记录一个 16 16 16为二进制数表示这个空格可以填哪些数,第 i i i位为 0 0 0表示第 i i i个字符未填.
然后我们就可以大力dfs了.不过可以先填当前可能性最少的空格,从而达到优化的效果.
发现还是不够快,继续考虑优化.
考虑对于每个空格,当这个空格 A . . P A..P A..P均不能填时,可以直接退出;当这个空格只有一个状态时,可以直接填入.
对于每一行,若某一个字母不能填入这一行中的任意一个空位就退出;若一个字母只能填入一个位置时,则直接填入.列与宫格同理也可以这样优化.
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=16,G=4;
char s[N][N];
int num[1<<N],cnt[1<<N],top;
int vd[N][N],now;
int cnt0(int x){return N-cnt[x];}
int cnt0(int x,int y){return cnt0(vd[x][y]);}
int get0(int x){int k=0;for (;k<N;++k) if (x>>k&1^1) return k;}
void fill(int x,int y,int z){ //将z填入(x,y)
++now;
s[x][y]=z+'A';
for (int i=0;i<N;++i) vd[x][i]|=1<<z,vd[i][y]|=1<<z;
x=x/G*G;y=y/G*G;
for (int i=0;i<G;++i)
for (int j=0;j<G;++j)
vd[x+i][y+j]|=1<<z;
}
int check_row(int x,int k){ //检查第x行中字母k的状态
int cnt=0,id;
for (int y=0;y<N;++y){
if (s[x][y]==k+'A') return -2;
if (s[x][y]^'-') continue;
//注意这里一定要continue,因为vd[x][y]在(x,y)已填的情况下依然会有忽略(x,y)的可选情况
if (vd[x][y]>>k&1^1) ++cnt,id=y;
}
if (!cnt) return -1;
if (cnt==1) return id;
return -2;
}
int check_col(int y,int k){ //检查第y列中字母k的状态
int cnt=0,id;
for (int x=0;x<N;++x){
if (s[x][y]==k+'A') return -2;
if (s[x][y]^'-') continue;
if (vd[x][y]>>k&1^1) ++cnt,id=x;
}
if (!cnt) return -1;
if (cnt==1) return id;
return -2;
}
void check_grd(int r,int c,int k,int &x,int &y){ //检查左上角为(r,c)的16宫格中字母k的状态
int cnt=0;
for (int i=0;i<G;++i)
for (int j=0;j<G;++j){
if (s[r+i][c+j]==k+'A') {x=-2;return;}
if (s[r+i][c+j]^'-') continue;
if (vd[r+i][c+j]>>k&1^1) ++cnt,x=r+i,y=c+j;
}
if (!cnt) x=-1;
if (cnt>1) x=-2;
}
bool dfs(){
if (now>=top) return 1;
//检查每个空位
for (int i=0;i<N;++i)
for (int j=0;j<N;++j){
if (s[i][j]^'-') continue;
if (!cnt0(i,j)) return 0;
if (cnt0(i,j)==1) fill(i,j,get0(vd[i][j]));
}
if (now>=top) return 1;
//检查每一行
for (int x=0;x<N;++x)
for (int k=0;k<N;++k){
int y=check_row(x,k);
if (y==-1) return 0;
if (y^-2) fill(x,y,k);
}
if (now>=top) return 1;
//检查每一列
for (int y=0;y<N;++y)
for (int k=0;k<N;++k){
int x=check_col(y,k);
if (x==-1) return 0;
if (x^-2) fill(x,y,k);
}
if (now>=top) return 1;
for (int r=0;r<N;r+=G)
for (int c=0;c<N;c+=G)
for (int k=0;k<N;++k){
int x,y;
check_grd(r,c,k,x,y);
if (x==-1) return 0;
if (x^-2) fill(x,y,k);
}
if (now>=top) return 1;
//备份
char ts[N][N];
int tvd[N][N],tnow;
for (int i=0;i<N;++i)
for (int j=0;j<N;++j)
tvd[i][j]=vd[i][j],ts[i][j]=s[i][j];
tnow=now;
//找可能方案最少的格子
int tmp=N+1,x,y;
for (int i=0;i<N;++i)
for (int j=0;j<N;++j){
if (s[i][j]^'-') continue;
if (!cnt0(i,j)) return 0;
if (tmp>cnt0(i,j)) x=i,y=j,tmp=cnt0(i,j);
}
//递归
for (int k=0;k<N;++k){
if (vd[x][y]>>k&1) continue;
fill(x,y,k);
if (dfs()) return 1;
now=tnow;
for (int i=0;i<N;++i)
for (int j=0;j<N;++j)
vd[i][j]=tvd[i][j],s[i][j]=ts[i][j];
}
return 0;
}
Abigail start(){
for (int i=0;i<1<<N;++i)
for (int j=i;j;j-=j&-j) ++cnt[i];
for (int i=0;i<N;++i) num[1<<i]=i;
}
Abigail into(){
for (int i=1;i<N;++i)
scanf("%s",s[i]);
}
Abigail work(){
for (int i=0;i<N;++i)
for (int j=0;j<N;++j)
vd[i][j]=0;
top=0;
for (int i=0;i<N;++i)
for (int j=0;j<N;++j)
if (s[i][j]=='-') ++top;
else fill(i,j,s[i][j]-'A');
now=0;dfs();
}
Abigail outo(){
for (int i=0;i<N;++i){
for (int j=0;j<N;++j)
putchar(s[i][j]);
puts("");
}
puts("");
}
int main(){
start();
while (~scanf("%s",s[0])){
into();
work();
outo();
}
return 0;
}