题目链接:http://poj.org/problem?id=3074
这是一道一看就知道的dfs,就是如何玄学的剪枝。
首先是对于顺序的剪枝,肯定可以知道的是可以从小到大进行,这样一定是最小的
因此,我们可以先进性一次性扫描,寻找最小的(如果有一个什么都不能填的就return)
就这么一个剪枝,就可已过了
但是还有一个问题,就是这样如果一个一个扫会很加大常数,所以就要用一个很玄学的东东
二进制(哈哈想不到吧)
#include<bits/stdc++.h>
using namespace std;
int lowbit(int x){return x&-x;}
int a[1000],c[1000],row[10],rank[10],jiu[10];
char write[90];
int nine(int x,int y){return x/3*3+y/3;}//寻找九宫格
int num(int x,int y){return x*9+y;}
int Cin(int x,int y,int z)
{
int xxx=(1<<z);
row[x]^=xxx;
rank[y]^=xxx;
jiu[nine(x,y)]^=xxx;
}
bool dfs(int cnt)
{
int Min=10000,x,y,z;
if(cnt==0)return 1;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(write[num(i,j)]=='.')
{
int now=row[i]&rank[j]&jiu[nine(i,j)];
//判断数字是否找过
if(now==0)return 0;//没有数字了
if(a[now]<Min)//寻找最小下标记录
{
Min=a[now];
x=i;y=j;z=now;
}
}
for(int i=z;i;i-=lowbit(i))//一个个枚举
{
int zz=c[lowbit(i)];
write[num(x,y)]=char(zz+49);
Cin(x,y,zz);
if(dfs(cnt-1))return 1;//成功了!!!
Cin(x,y,zz);//回溯 (没写100分没了)
write[num(x,y)]='.';
}
return 0;
}
void init()//预处理
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
for(int i=1;i<(1<<9);i++)
for(int j=i;j;j-=lowbit(j))
a[i]++;//a[i]表示二进制i中1的个数
for(int i=0;i<9;i++)
c[1<<i]=i;//c[1<<i]表示这个二进制中的第一个i
cin>>write;
}
int sum;
void work()
{
while(write[0]!='e'&&write[1]!='n'&&write[2]!='d')
{
sum=0;
for(int i=0;i<9;i++)row[i]=rank[i]=jiu[i]=(1<<9)-1;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(write[num(i,j)]!='.')Cin(i,j,write[num(i,j)]-'1');
else sum++;
dfs(sum);
cout<<write<<endl;
cin>>write;
}
}
int main()
{
init();
work();
return 0;
}