题面
题解
通过位运算表示描绘出当前的局面,某一行某一列某一某一宫格均用1个长度是9为的二进制数表示,若当前位置是1表示该行该列或者该宫格可填入
在选择当前需要填数的位置时,选择分支最少的格子
state = row[x] & col[y] & cell[x / 3][y / 3]表示当前位置(x,y)可填数的状态,某二进制为是1表示可填该数
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=9,M=1<<N;
char str[100];
int row[N],col[N],cell[3][3]; //行列九宫格中数字的状态
int ones[M]; //每个状态1的个数
int map[M];
int lowbit(int x){
return x&-x;
}
void init(){
//初始化所有位置都是1(表示可以填任意数字)
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;
}
}
}
void draw(int x,int y,int t,bool is_set){
if(is_set) str[x*N+y]='1'+t; //填数字
else str[x*N+y]='.'; //恢复现场
//更新状态
int v=1<<t;
if(!is_set) v=-v;
row[x]-=v;
col[y]-=v;
cell[x/3][y/3]-=v;
}
//返回可以填哪几个数的二进制状态
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;
int x,y;
//优先搜索分支少的(1的个数少的)
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(str[i*N+j]=='.'){
int state=get(i,j);
if(ones[state]<minv){
minv=ones[state];
x=i,y=j;
}
}
}
}
int state=get(x,y);
for(int i=state;i;i-=lowbit(i)){
int t=map[lowbit(i)];
draw(x,y,t,true);
if(dfs(cnt-1)) return true;
draw(x,y,t,false); //恢复现场
}
return false;
}
int main(){
//预处理一个数是2的多少次方
for(int i=0;i<N;i++) map[1<<i]=i;
//预处理每个状态1的个数
for(int i=0;i<M;i++){
for(int j=i;j;j-=lowbit(j)){
ones[i]+=1;
}
}
while(cin>>str,str[0]!='e'){
init();
int cnt=0; //有多少个空位置
int k=0;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(str[k]!='.'){
int t=str[k]-'1';
draw(i,j,t,true);
}else{
cnt++;
}
k++;
}
}
dfs(cnt);
cout<<str<<endl;
}
return 0;
}