Sudoku

Sudoku Solver with DFS and Bit Manipulation

题目链接: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值