POJ2676/3074 Sudoku

在这里插入图片描述

analysis

题目意思就是以各种方式给你一个9x9的数独让你填
然后数据是各种鬼畜

这个时候考虑一个很简单的搜索框架:就是找一个位置的可能数,然后暴力的dfs

然后考虑剪枝和优化:

想要优化这个算法,想了一个策略:找可选的数最少的一个

但是如何统计可选的数呢,用数组?统计一次就O(n)未必太慢了,用状态压缩的方法就可以,因为这里的数据范围是这样的小

我们可以用状态压缩表示每行每列每个九宫格的数字选取情况,但是有没有什么办法能够让我们快速的找到当前位置可以选的数呢?这个时候就有一个比较好的方法,就是在表示选取状况的时候,如果有这个数,就表示为0,反之表示为1,这样的话就可以用&运算来取得没有选的数的集合了,在加上lowbit,简直快的不要不要的,时间复杂度就是O(1)

但为了选取候选数最少的那个,我们还需要快速算出当前二进制集合里面1的个数

并且我们还要把当前二进制下选的数转化成10进制(100=>3)

于是想到,可以预处理数组,这个问题就迎刃而解

于是就可以过了

具体实现的时候要注意一个问题:1<<x是指将1左移x位,也就是说单纯的1是1<<0而非1<<1,所以这个时候最好用0基准来写代码

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
#include <bitset>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
#define loop(i,start,end) for(int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(int i=start;i>=end;--i) 
#define clean(arry,num) memset(arry,num,sizeof(arry)) 
#define ll long long
template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
	while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
	x*=neg;
}
#define lowbit(x) (x&(-x))
int T;
char s[100];
inline bool readsd(){
	int cnt=0;char r;
	while(1){
		if(cnt>=81)break;
		r=getchar();
		if(r>='0'&&r<='9')s[cnt++]=r;
		else if(r=='.')s[cnt++]='0';
		else if(r=='e')return false;
	}
	return true;
}

int x[15],y[15],z[15];
int posx,posy,posz;
int cnt[(1<<10)+10];
int f[(1<<10)+10];
inline void getpos(int _x){
	posx=(_x)/9;
	posy=(_x)%9;
	posz=(posx/3)*3+posy/3;
}
inline void turn(int _x,int w){
	getpos(_x);
	--w;
	x[posx]^=(1<<w);
	y[posy]^=(1<<w);
	z[posz]^=(1<<w);
}

bool dfs(int pos){
	if(!pos)
		return true;
	int bestpos=0,minn=10;
	loop(i,0,80){
		if(s[i]=='0'){
			getpos(i);
			int _w=x[posx]&y[posy]&z[posz];
			if(minn>cnt[_w]){
				minn=cnt[_w];
				bestpos=i;
			}
		}
	}
	getpos(bestpos);
	int w_=x[posx]&y[posy]&z[posz];
	while(w_){
		int num=f[lowbit(w_)]+1;
		s[bestpos]=num+'0';
		turn(bestpos,num);
		if(dfs(pos-1))
			return true;
		s[bestpos]='0';
		turn(bestpos,num);
		w_-=lowbit(w_);
	}
	return false;
}

inline int count_(int x){
	int res=0;
	while(x){
		x-=lowbit(x);
		++res;
	}
	return res;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("datain.txt","r",stdin);
	#endif
	while(readsd()){
		int nfz=81;
		loop(i,0,9)
			x[i]=y[i]=z[i]=(1<<9)-1,
			f[1<<i]=i;
		loop(i,0,80)
			if(s[i]!='0')
				turn(i,s[i]-'0'),
				--nfz;
		loop(i,0,(1<<9))
			cnt[i]=count_(i);
		if(!dfs(nfz)){
			printf(">>>>>>>>>>>WA\n");
			continue;
		}
		loop(i,0,80)
			printf("%c",s[i]);
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值