蓝桥杯 方块填数 数独 dfs 状压 c++

  “数独”是当下炙手可热的智力游戏。一般认为它的起源是“拉丁方块”,是大数学家欧拉于1783年发明的。
    如图[1.jpg]所示:6x6的小格被分为6个部分(图中用不同的颜色区分),每个部分含有6个小格(以下也称为分组)。
    开始的时候,某些小格中已经填写了字母(ABCDEF之一)。需要在所有剩下的小格中补填字母。


    全部填好后,必须满足如下约束:
    1. 所填字母只允许是A,B,C,D,E,F 中的某一个。
    2. 每行的6个小格中,所填写的字母不能重复。
    3. 每列的6个小格中,所填写的字母不能重复。
    4. 每个分组(参见图中不同颜色表示)包含的6个小格中,所填写的字母不能重复。

用下面的数据表示其已有字母的填写情况:
02C
03B
05A
20D
35E

53F

    很明显,第一列表示行号,第二列表示列号,第三列表示填写的字母。行号、列号都从0开始计算。
    一种可行的填写方案(此题刚好答案唯一)为:
E F C B D A
A C E D F B
D A B E C F
F B D C A E
B D F A E C
C E A F B D

    你的任务是:编写程序,对一般的拉丁方块问题求解,如果多解,要求找到所有解。


深搜时对 行 列 分组 三个flag(fr fc fg)状压判断是否满足条件进行剪枝。

这题如果没有条件4(分组限制)就很容易做了,所以关键就是怎么快速判断分组是否满足条件。

这里用了一个map做了个简单的哈希表,每个位置[i][j]在map中都有对应(即:m[i*10+j])。在输入分组时就将每个位置的map指向对应的分组标志(fg)上,即为程序中 m[i*10+j]=c-'0' 语句(c-'0'即为fg下标)。此时fg[m[i*10+j]]就能方便记录和查看该组已有字母。理解这关键的一点,其他的就好说了。


上代码:

#include <iostream>
#include <map>
using namespace std;

map<int,int>m;
int a[6][6],v[6][6];
int fr[6],fc[6],fg[6];
int cnt;

void dfs(int x,int y)
{
	if(x>=6)
	{
		cout<<++cnt<<endl;
		for(int i=0;i<6;i++)
		{
			for(int j=0;j<6;j++)
				cout<<(char)(a[i][j]+'A')<<' ';
			cout<<endl;
		}
		return ;
	}
	if(y>=6)
	{
		dfs(x+1,0);
		return;
	}
	if(v[x][y])
	{
		dfs(x,y+1);
		return; 
	}
	
	for(int i=0;i<6;i++)
	{
		if(fr[x]&(1<<i)) continue;
		if(fc[y]&(1<<i)) continue;
		if(fg[m[x*10+y]]&(1<<i)) continue;
		
		fr[x]^=1<<i;
		fc[y]^=1<<i;
		fg[m[x*10+y]]^=1<<i;
		v[x][y]=1;
		a[x][y]=i;
		
		dfs(x,y+1);
		
		fr[x]^=1<<i;
		fc[y]^=1<<i;
		fg[m[x*10+y]]^=1<<i;
		v[x][y]=0;
		a[x][y]=0;
	}
}

int main()
{
	char c,x,y;
	int i,j,n;
	for(i=0;i<6;i++)
	{
		for(j=0;j<6;j++)
		{
			cin>>c;
			m[i*10+j]=c-'0';
		}
	}
	cin>>n;
	while(n--)
	{
		cin>>x>>y>>c;
		x-='0';
		y-='0';
		c-='A';
		a[x][y]=c;
		v[x][y]=1;
		fr[x]^=1<<c;
		fc[y]^=1<<c;
		fg[m[x*10+y]]^=1<<c;
	}
    dfs(0,0);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值