题解 P1784 【数独】

看完所有的题解之后发现没有人用二进制,我就知道我的机会来了(手动滑稽)

在做这一题之前你所需要知道的二进制操作

x << y//相当于x * pow(2,y)
lowbit(x)//找到x的二进制中最小的1的位置
1 & 1 = 1
0 & 1 = 0
0 & 0 = 0
//没有了

  关于这一题,line,rool,cell三个数组(均初始化为111111111(二进制))分别用来存这一行、列、九宫格(以0开始,方便九宫格的操作)还可以存的数有哪些,存在相对应的二进制位上。

  为了得到某一个格子的可以填的数,只需要一个get函数就可以解决。

inline int get(int x,int y){
	return line[x]&roll[y]&cell[x/3][y/3];
}

最重要的,注意dfs的顺序(剪枝必备)

  就像我们平时做数独一样,从可以填的数最少的格子开始枚举(这一题其实不需要,但是靶形数独一定是要这样剪枝的(可能是强度的问题))

  话不多说,上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define _ 0
const int N=9;
int a[N][N];
int line[N],roll[N],cell[N/3][N/3];
int map[1<<N],once[1<<N];//map是为了得到lowbit的对应的位数,once方便找这一格可以填的数的个数
inline void init(){
	for(int i=0;i<N;i++)
		line[i]=roll[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 lowbit(int x){//定义lowbit函数
	return x&(-x);
}
inline int get(int x,int y){//定义get函数,前面已经介绍过
	return line[x]&roll[y]&cell[x/3][y/3];
}
bool dfs(int cnt){
	if(!cnt) return true;
	int minn=10;
	int x,y;
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++){
			if(a[i][j]==0)
			if(minn>once[get(i,j)]) x=i,y=j,minn=once[get(i,j)];
		}//找到可填的数最少的一格
	int xx=get(x,y);
	for(int i=xx;i;i-=lowbit(i)){
		int l=map[lowbit(i)];
		a[x][y]=l+1;
		line[x] -= 1 << l;
		roll[y] -= 1 << l;
		cell[x/3][y/3] -= 1 << l;
		if(dfs(cnt-1)) return true;
		line[x] += 1 << l;
		roll[y] += 1 << l;
		cell[x/3][y/3] += 1 << l;
		a[x][y]=0;
	}
	return false;
}
int main(){
	for(int i=0;i<9;i++){//初始化map
		map[1<<i]=i;
	}
	for(int i=0;i<(1<<N);i++){//初始化once
		int s=0;
		for(int j=i;j;j-=lowbit(j)){
			s++;
		}
		once[i]=s;
	}
	init();//初始化line,roll,cell
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++){
			scanf("%d",&a[i][j]);
		}
	int cnt=0;
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++){
			if(!a[i][j]) cnt++;
			else{
				int t=a[i][j]-1;
				line[i]-=1<<t;
				roll[j]-=1<<t;
				cell[i/3][j/3]-=1<<t;
			}
		}
	dfs(cnt);
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++)
			printf("%d ",a[i][j]);
		putchar('\n');
	}
	return ~~(0^_^0);//卖个萌
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值