1. 程式人生 > >69. [NOIP2004] 蟲食算

69. [NOIP2004] 蟲食算

必須 返回 不一定 frame 註定 不同的 發現 個數字 %d

69. [NOIP2004] 蟲食算

★★★ 輸入文件:alpha.in 輸出文件:alpha.out 簡單對比
時間限制:1 s 內存限制:128 MB

【問題描述】

所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看一個簡單的例子:

43#9865#045
+ 8468#6633

-----------

44445509678

其中#號代表被蟲子啃掉的數字。根據算式,我們很容易判斷:第一行的兩個數字分別是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。

又填了一個坑,好爽,貼代碼

技術分享
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,s[30],book,h[30];
 4 char a[90],b[30],c[30];
 5 void dfs(int x,int y,int z){
 6     if(book)return ;
 7     for(int i=1;i<=x;i++){
 8         if(s[a[i]-A]==-1||s[a[i+n]-A]==-1||s[a[i+2*n]-A]==-1)continue;
 9         if
((s[a[i]-A]+s[a[i+n]-A]+1)%n!=s[a[i+2*n]-A]&&(s[a[i]-A]+s[a[i+n]-A])%n!=s[a[i+2*n]-A])return ; 10 }//每次都對所有位進行判斷,因為加法最多對下一位加一,所以可以特判一下,如果有一位3個數都被附上值了,而上面兩個相加不等於第三個,加一之後也不等於第三個,那麽這種情況肯定不合法 11 if(!x){ 12 if(!y){ 13 for(int i=0;i<n;i++)cout<<s[i]<<" "; 14 book=1; 15 } 16 return ; 17 } 18 if(z!=3){ 19 if(s[a[(z-1)*n+x]-A]==-1){ 20 for(int i=0;i<n;i++){ 21 if(h[i])continue; 22 h[i]=1;s[a[(z-1)*n+x]-A]=i; 23 dfs(x,y,z+1); 24 h[i]=0;s[a[(z-1)*n+x]-A]=-1; 25 } 26 } 27 else dfs(x,y,z+1); 28 } 29 if(z==3){ 30 if(s[a[2*n+x]-A]==-1){ 31 int tem1=s[a[x]-A]+s[a[x+n]-A]+y; 32 if(!h[tem1%n]){ 33 s[a[2*n+x]-A]=tem1%n;h[tem1%n]=1; 34 dfs(x-1,tem1/n,1); 35 s[a[2*n+x]-A]=-1;h[tem1%n]=0; 36 } 37 } 38 else if((s[a[x]-A]+s[a[x+n]-A]+y)%n==s[a[x+2*n]-A])dfs(x-1,(s[a[x]-A]+s[a[x+n]-A]+y)/n,1); 39 } 40 } 41 int main() 42 { 43 freopen("alpha.in","r",stdin); 44 freopen("alpha.out","w",stdout); 45 // freopen("1.txt","r",stdin); 46 scanf("%d",&n); 47 scanf("%s%s%s",a+1,b+1,c+1); 48 for(int i=n+1;i<=2*n;i++)a[i]=b[i-n]; 49 for(int i=n*2+1;i<=3*n;i++)a[i]=c[i-2*n]; 50 memset(s,-1,sizeof(s)); 51 dfs(n,0,1); 52 return 0; 53 }
View Code

這道題我暑假就在做了,不過當時寫了個100多行的長代碼,,樣例都沒過,,然後就出去旅遊了,就丟到現在了...這道題我今天寫一共才用了50行,其實換一種寫法這道題還是很簡單的

最基本的思路,從最低位開始枚舉,符合就繼續往下搜,這裏可以像我那樣寫,dfs多引入一個變量,會大大減少代碼量,將a數組換成一個二維數組效果會更好。這樣的話是可以拿到80分的

然後我看評論區dalao們的評論,又加了一個小剪枝,我本來對於第三個字符串處理的時候,是枚舉所有可能情況,通過第一個和第二個對應位判斷是否可行,,,然而並不需要這樣,直接通過

前兩位就可以推出第三位,這樣會減少特判的時間以及for循環中++的時間,可以多過一個點,90分,第8個點非常毒瘤,我本地跑了1分鐘,實在不知道怎麽剪枝了,又去看了評論

我看的達哥的評論,剛開始還想怎麽實現,後來發現其實特別簡單。

引入達哥的話:“我是從右向左(也就是從低位到高位)深搜的,這樣會導致有時的選擇在高位的地方產生矛盾,卻必須搜到高位才能剪掉,此時這個註定錯誤的選擇已經形成了一棵很大的搜索樹.只要每新選擇一個數就判斷高位是否產生矛盾即可”

對於任意一位,他對下一位的進位最多就是1,因為是加法。所以在每一次dfs中,我們特判一下那些已經確定了的位,判斷一下第一個和第二個對應位加起來是不是等於第三個對應位,再判一下加一後是否符合(要對n取模),如果都不符合,那麽這種情況一定是不合法的,就沒有必要繼續搜下去了,直接返回即可。

加了這個優化之後速度飆起來了,60秒變0.6秒。

69. [NOIP2004] 蟲食算