YBTOJ 虫食算
好吧这题正解其实是高斯消元,但是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;
}