为了一劳永逸而写的数独

在这里插入图片描述

166. 数独

显然要用到DFS,对于每个未填的空格,要排除掉同行同列同九宫内出现过的数字。不加优化不出意外的话会T飞︿( ̄︶ ̄)︿。

考虑剪枝:

  1. 优化搜索顺序
  2. 排除冗余信息
  3. 可行性剪枝(照当前分支这样走后面不可能达成)
  4. 最优性剪枝(照当前分支这样走的最优解都比当前解差)
  5. 记忆化搜索

这里我们主要考虑优化搜索顺序。这其实是跟大家做数独的时候的朴素感知是一样的,从可选的数少的空格开始试探。所以我们在每次搜索时都判断一下哪个格子的可选到数最小(可以预先处理出来可能会用到的所有二进制数里1的个数,用时直接调用)。

记录每个格子可选的数,可以用二进制状态压缩,开九位表示九个数。为了方便,我们把1~9映射成0 ~8。开三个数组 r o w [ N ] , c o l [ N ] , c e l l [ N / 3 ] [ N / 3 ] row[N],col[N],cell[N/3][N/3] row[N],col[N],cell[N/3][N/3]分别记录每行、每列、每个九宫格内有哪些数不能填。对于位置在 ( x , y ) (x,y) (x,y)的数,用 row[x] & col[y] & cell[x/3][y/3] 可以查到该空格可以填哪些数,那就老老实实搜索这些数吧233,可以用lowbit获得二进制下为1的每一位,将其转化成十进制数(1的位置对应的位数,可以预先处理出来)。搜索到每一个数时改变一下row.col.cell的状态,回溯的时候复原。

#include<bits/stdc++.h> 
using namespace std;
const int N=9;
string str;
int mp[1<<N],nums[1<<N],row[N],col[N],cell[N][N];

inline int lowbit(int x)
{
	return x&(-x);
}

int get(int x,int y)
{
	return row[x]&col[y]&cell[x/3][y/3];//求出这一个位置可能会被填哪些数字 
}

void init()//初始化:所有位所有数字都可以填 
{
	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;
}

bool dfs(int cnt)
{
	if(cnt==0) return true;//所有待填数字都已经被填上 
	int minn=10,x,y;
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
		{
			if(str[i*9+j]=='.')
			{
				int temp=nums[get(i,j)];
				if(temp<minn)
				{
					minn=temp;
					x=i,y=j;
				}
			}
		}//找到所有剩下的待填格中数字集合最小的一个,是一个剪枝过程 
	for(int i=get(x,y);i;i-=lowbit(i))//枚举这一格所有可能填的数字 
	{
		int t=mp[lowbit(i)];
		row[x]-=(1<<t);
		col[y]-=(1<<t);
		cell[x/3][y/3]-=(1<<t);
		str[x*9+y]='1'+t;
		if(dfs(cnt-1)) return true;
		row[x]+=(1<<t);
		col[y]+=(1<<t);
		cell[x/3][y/3]+=(1<<t);
		str[x*9+y]='.';//复原 
	}
	return false;
}


int main()
{
	for(int i=0;i<N;i++) mp[1<<i]=i;//预先存下二进制1所在的位置 
	for(int i=1;i<(1<<N);i++)
		for(int j=i;j;j-=lowbit(j)) nums[i]++;
		//预先存下每个之后可能会枚举到的二进制数内有几个1 
	while(cin>>str&&str[0]!='e')
	{
		init();
		int cnt=0;
		for(int i=0,k=0;i<9;i++)
			for(int j=0;j<9;j++,k++)
			{
				if(str[k]!='.')
				{
					int t=str[k]-'1';//1~9映射到0~8 
					row[i]-=(1<<t);
					col[j]-=(1<<t);
					cell[i/3][j/3]-=(1<<t);
				}//这一位数字是题给的,把它存进对应位置 
				else cnt++;//待填数字+1 
			}
		dfs(cnt);
		cout<<str<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

春弦_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值