NOIP2004提高组 虫食算
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
43#9865#045
+ 8468#6633
44445506978
其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。输入数据保证N个字母分别至少出现一次。
BADC
+ CBDA
DCCC
上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解,
输入包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
输出包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
5
ABCED
BDACE
EBBAA
1 0 3 4 2
对于30%的数据,保证有N<=10;
对于50%的数据,保证有N<=15;
对于全部的数据,保证有N<=26。
这是一道令人发指的搜索题,
首先,很容易想到的就是从低位到高位搜索,这个我也一开始也想到了,
其次,就是剪枝了。很容易想到的是,如果当前位上三个数都已知但a+b+进位≠c,要剪枝,但是事实证明,这样只能得60分,于是乎,我就卡在这里了,我后来想过好多好多剪枝,但不是因为太麻烦就是因为效果差而放弃了,事实上,我已经离成功很近了
能够想到对当前位上等式的判断,为何就想不到对所有位上都做一次这样的判断呢,于是就产生了check()函数,每次进入dfs(),都进行一次check()操作:
枚举所有的位,对于某一位,若a、b、c都已知,且有((a+b)%N≠c and (a+b+1)%N≠c),则当前状态需要剪枝
于是,就愉快的AC了
实际上,还有一个剪枝(比较复杂),但是这样已经能AC了,就不费那个功夫了
代码:
//NOIP2004 虫食算 搜索 #include <cstdio> #include <cstdlib> #include <cstring> #define maxn 50 #define unknow -1 using namespace std; char A[maxn], B[maxn], C[maxn], sum, **hash[5]; int num[500], N, shi[5][maxn]; bool used[maxn]; void show() { int i; printf("%d",num['A']); for(i='B';i<'A'+N;i++) printf(" %d",num[i]); printf("\n"); exit(0); } bool check() { int i, j, a, b, c, s; for(i=1;i<=N;i++) { a=shi[1][i]; b=shi[2][i]; c=shi[3][i]; if(a==unknow || b==unknow || c==unknow) continue; s=(a+b)%N; if(s!=c && (s+1)%N!=c)return false; } return true; } void change(const char c, const int k) { int i; for(i=1;i<=N;i++)if(A[i]==c)shi[1][i]=k; for(i=1;i<=N;i++)if(B[i]==c)shi[2][i]=k; for(i=1;i<=N;i++)if(C[i]==c)shi[3][i]=k; } void dfs(int d,int jin) { if(d==0 && jin==0)show(); if(!check())return; int i, j, k, t, fa, fb, fc; fa=(num[A[d]]==unknow); for(i=0;i<N;i++) { if(fa) if(!used[i]) { num[ A[d] ]=i; used[i]=true; change(A[d],i); } else continue; fb=(num[ B[d] ]==unknow); for(j=0;j<N;j++) { if(fb) if(!used[j]) { num[ B[d] ]=j; used[j]=true; change(B[d],j); } else continue; fc=(num[ C[d] ]==unknow); t=shi[1][d]+shi[2][d]+jin; k=t/N; t%=N; if(fc) { if(!used[t]) { num[ C[d] ]=t; used[t]=true; change(C[d],t); dfs(d-1,k); num[ C[d] ]=unknow; used[t]=false; change(C[d],unknow); } } else { if(num[ C[d] ]==t) dfs(d-1,k); } if(fb) { num[ B[d] ]=unknow; used[j]=false; change(B[d],unknow); } else break; } if(fa) { num[ A[d] ]=unknow; used[i]=false; change(A[d],unknow); } else break; } } int main() { char s[50], i, j; memset(num,unknow,sizeof(num)); memset(shi,unknow,sizeof(shi)); scanf("%d\n",&N); scanf("%s",s);for(i=0;i<N;i++) A[i+1]=s[i]; scanf("%s",s);for(i=0;i<N;i++) B[i+1]=s[i]; scanf("%s",s);for(i=0;i<N;i++) C[i+1]=s[i]; dfs(N,0); return 0; }