題意:傳送門
題解:首先考慮一種策略,先填上“已經能夠唯一確定的位置”,然後從那些填的比較滿,選項比較少的位置實施突破。這樣使得搜索樹的規模大大降低,其次考慮的就是在搜索狀態上的記錄,檢索和更新上的開銷(影響程序運行的“常數”時間),可以使用位運算來代替數組執行"對各個位置所填數字的記錄"以及"可填性的檢查與統計",這樣就代替了使得。
算法過程是:
- 對於每行每列,每個九宮格,分別用一個位二進制數保存哪些數字還可以填入。
- 對於每個位置,把它所在行、列、九宮格的個二進制數做運算,得出該位置要填哪些數,用運算就可以把能填的數取出。
- 優先搜索可填數字少的,當一個位置填上某些數之後,把該位置的行、列、九宮格記錄的二進制數的對應位置改爲,即可更新狀態,回溯時改爲恢復現場。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=9;
int row[N],col[N],cell[5][5],ones[1<<N],map[1<<N];
char str[100];
inline int lowbit(int x){return x&-x;}
void init()
{
for(int i=0;i<N;i++)row[i]=col[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 x,int y){return row[x]&col[y]&cell[x/3][y/3];}
bool dfs(int cnt)
{
if(!cnt)return true;
int minv=10,x,y;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(str[i*9+j]=='.'){
int t=ones[get(i,j)];
if(minv>t){
minv=t;x=i;y=j;
}
}
}
}
for(int i=get(x,y);i;i-=lowbit(i)){
int t=map[lowbit(i)];
row[x]-=(1<<t);
col[y]-=(1<<t);
cell[x/3][y/3]-=(1<<t);
str[x*9+y]='1'+t;
if(dfs(cnt-1))return true;
row[x]+=(1<<t);
col[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;
for(int i=0;i<1<<N;i++)ones[i]=ones[i>>1]+i%2;
while(scanf("%s",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';
row[i]-=1<<t;
col[j]-=1<<t;
cell[i/3][j/3]-=1<<t;
}else cnt++;
}
}
dfs(cnt);
cout<<str<<endl;
}
return 0;
}