[NOIP2004]虫食算
时间限制: 1 Sec 内存限制: 64 MB
题目描述
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
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个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。
输入
输入文件alpha.in包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
输出
输出文件alpha.out包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
样例输入
5
ABCED
BDACE
EBBAA
样例输出
1 0 3 4 2
【数据规模】
对于30%的数据,保证有N≤10;
对于50%的数据,保证有N≤15;
对于全部的数据,保证有N≤26。
看到这道题,先把暴力打出来。
就是枚举每一个字母所对应的数字,然后在枚举完后check一下就行了。
然后想剪枝,
(1)我们不妨从低位字母向高位字母枚举,如果有从第一位开始连续的 几列等式字母都枚举过,我们不妨模拟竖式计算,若发现矛盾
(A[i]+B[i]+余数) mod N≠C[i]
,就剪枝。
(2)对于某一列上只缺一个数的情况,我们可以根据另外两个数求出所缺数的近似值。
已知A[i],B[i]:
C[i]=(A[i]+B[i])% N 或(A[i]+B[i]+1)%N
已知A[i],C[i]:
B[i]=(C[i]−A[i]+N)% N 或(C[i]−A[i]−1+N)%N
已知B[i],C[i]:
C[i]=(C[i]−B[i]+N)% N 或(C[i]−B[i]−1+N)%N
如果求出来的近似值都出现过,那么便剪掉。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 26
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;
char A[MAXN+5],B[MAXN+5],C[MAXN+5];
char words[MAXN+5];int cnt;
char keys[MAXN+5];
bool used[MAXN+5];
int N;
void print()
{
for(int i=0;i<N-1;++i)
printf("%d ",keys[i]);
printf("%d\n",keys[N-1]);
return;
}
void change(char *str,char f,char t)
{
for(int i=1;i<=N;++i)
str[i]=(str[i]==f?t:str[i]);
}
bool check1()
{
int res=0;
for(int i=N;i>=1;--i)
{
if(A[i]>=N||B[i]>=N||C[i]>=N)
return true;
res=A[i]+B[i]+res;
if(res%N!=C[i])
return false;
res/=N;
}
return true;
}
bool check2()
{
int p,p1,p2;
for(int i=N;i>=1;--i)//a,b,c
{
if(A[i]>=N||B[i]>=N||C[i]>=N)
continue;
p=(A[i]+B[i])%N;
if(!(p==C[i]||(p+1)%N==C[i]))
return false;
}
for(int i=N;i>=1;--i)//a,b,?
{
if(A[i]>=N||B[i]>=N||C[i]<N)
continue;
p1=(A[i]+B[i])%N;
p2=(p1+1)%N;
if(used[p1]&&used[p2])
return false;
}
for(int i=N;i>=1;--i)//a,?,c
{
if(A[i]>=N||B[i]<N||C[i]>=N)
continue;
p1=(C[i]-A[i]+N)%N;
p2=(p1-1)%N;
if(used[p1]&&used[p2])
return false;
}
for(int i=N;i>=1;--i)//?,b,c
{
if(A[i]<N||B[i]>=N||C[i]>=N)
continue;
p1=(C[i]-B[i]+N)%N;
p2=(p1-1)%N;
if(used[p1]&&used[p2])
return false;
}
return true;
}
bool dfs(int now)
{
if(!check1()||!check2())
return false;
if(now>cnt)
{
print();
return true;
}
for(int i=N-1;i>=0;--i)
if(!used[i])
{
used[i]=1;
keys[words[now]-'A']=i;
change(A,words[now],i);
change(B,words[now],i);
change(C,words[now],i);
if(dfs(now+1))
return true;
change(A,i,words[now]);
change(B,i,words[now]);
change(C,i,words[now]);
used[i]=0;
}
return false;
}
int main()
{
scanf("%d",&N);
scanf("%s%s%s",A+1,B+1,C+1);
cnt=0;
for(int i=N;i>=1;--i)
{
if(!used[A[i]-'A'])words[++cnt]=A[i],used[A[i]-'A']=1;
if(!used[B[i]-'A'])words[++cnt]=B[i],used[B[i]-'A']=1;
if(!used[C[i]-'A'])words[++cnt]=C[i],used[C[i]-'A']=1;
}
memset(used,0,sizeof(used));
dfs(1);
}