[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個字母分別至少出現一次。
—————-
上面的算式是一個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
【數據規模】
看到這道題,先把暴力打出來。
就是枚舉每一個字母所對應的數字,然後在枚舉完後check一下就行了。
然後想剪枝,
(1)我們不妨從低位字母向高位字母枚舉,如果有從第一位開始連續的 幾列等式字母都枚舉過,我們不妨模擬豎式計算,若發現矛盾
(2)對於某一列上只缺一個數的情況,我們可以根據另外兩個數求出所缺數的近似值。
已知A[i],B[i]:
已知A[i],C[i]:
已知B[i],C[i]:
如果求出來的近似值都出現過,那麼便剪掉。
#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);
}