YBTOJ 虫食算

这篇博客详细介绍了如何使用高斯消元和DFS剪枝解决洛谷P1072问题。作者首先解释了题目要求,然后分析了如何通过枚举字母代表的数值并利用剪枝策略来优化搜索过程。关键在于从低位到高位判断,并考虑进位情况。代码中展示了具体的实现,包括检查当前情况是否可行的逻辑。最后,通过DFS遍历所有可能的字母分配,实现了问题的解决方案。
摘要由CSDN通过智能技术生成

好吧这题正解其实是高斯消元,但是dfs剪枝也能过。

题目链接:洛谷P1072虫食算

题目要素:dfs剪枝

题目分析:

很显然要枚举每一个字母代表的数是什么,注意一个字母可以代表0。
然后考虑如何剪枝:直接判断每一种情况在当前是否可以判断它不成立,以达到剪枝的效果。

我们考虑如何判断当前情况不成立:
(1)肯定要从低位到高位判断,因为要考虑进位的问题。
(2)因为在当前情况下,可能高位的字母被确定后,低位反而没有被确定,因此需要分进位是否被确定两种情况。
还可以发现:第一位没有进位,因此需要特殊考虑。
✧✧✧✧<1>如果进位x被确定了:
✧✧✧✧✧✧✧✧✧【1】在第一位:a+b>=n && (a+b)%n = c 即成立
✧✧✧✧✧✧✧✧✧【2】在其他位:(a+b+x)%n = c即可
✧✧✧✧<2>如果进位x没有被确定,那么进位只可能是0或者1
✧✧✧✧✧✧✧✧✧【1】在第一位:a+b>=n && ((a+b+0)%n = c || (a+b+1)%n = c)) 则成立
✧✧✧✧✧✧✧✧✧【2】在其他位:(a+b+0)%n = c || (a+b+1)%n = c)即成立

(3)还有一个细节:
因为要枚举每一个字母出现于什么位置,就可以知道先讨论在低位出现的字母能较早地发现不合理的情况,从而进一步优化。
并不是不这么写会TLE掉3个点

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=35;
int n,vis[maxn],num[maxn],cnt;
bool used[maxn];
char s[maxn][maxn],ch[maxn];
bool check()
{
	int x=0;
	for(int i=n;i>=1;--i)
	{
		int a=num[s[1][i]-'A'];
		int b=num[s[2][i]-'A'];
		int c=num[s[3][i]-'A'];
		if(a!=-1 && b!=-1 && c!=-1)
		{
			if(x!=-1)
			{
				if((a+b+x)%n != c) return false;
				if(i==1 && a+b+x>=n) return false;
				x=(a+b+x)/n;	
			}
			else
			{
				if((a+b+0)%n!=c && (a+b+1)%n!=c) return false;
				if(i==1 && a+b>=n) return false;	
			}
		}
		else x=-1;
	}
	return true;
}
bool dfs(int x)
{
	if(x==cnt+1) return true;
	for(int i=0;i<n;++i)
	{
		if(!used[i])
		{
			num[ch[x]-'A']=i;
			used[i]=true;
			if(check() && dfs(x+1)) return true;
			num[ch[x]-'A']=-1;
			used[i]=false;
		}
	}
	return false;
}
int main()
{
	memset(num,-1,sizeof(num));
	scanf("%d",&n);
	for(int i=1;i<=3;++i) scanf("%s",s[i]+1);  
	for(int j=n;j>=1;--j)
		for(int i=1;i<=3;++i)	
			if(!vis[s[i][j]-'A'])
			{
				vis[s[i][j]-'A']=1;
				ch[++cnt]=s[i][j];	
			}
	dfs(1);
	for(int i=0;i<n;++i) printf("%d ",num[i]); 
	return 0;	
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值