洛谷 P1092 蟲食算
P1092 蟲食算
題目描述
所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看一個簡單的例子:
http://paste.ubuntu.com/25448822/
其中#號代表被蟲子啃掉的數字。根據算式,我們很容易判斷:第一行的兩個數字分別是5和3,第二行的數字是5。
現在,我們對問題做兩個限制:
首先,我們只考慮加法的蟲食算。這裏的加法是N進制加法,算式中三個數都有N位,允許有前導的0。
其次,蟲子把所有的數都啃光了,我們只知道哪些數字是相同的,我們將相同的數字用相同的字母表示,不同的數字用不同的字母表示。如果這個算式是N進制的,我們就取英文字母表午的前N個大寫字母來表示這個算式中的0到N-1這N個不同的數字:但是這N個字母並不一定順序地代表0到N-1)。輸入數據保證N個字母分別至少出現一次。
http://paste.ubuntu.com/25448824/
上面的算式是一個4進制的算式。很顯然,我們只要讓ABCD分別代表0123,便可以讓這個式子成立了。你的任務是,對於給定的N進制加法算式,求出N個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解
輸入輸出格式
輸入格式:
包含四行。第一行有一個正整數N(N<=26),後面的3行每行有一個由大寫字母組成的字符串,分別代表兩個加數以及和。這3個字符串左右兩端都沒有空格,從高位到低位,並且恰好有N位。
輸出格式:
包含一行。在這一行中,應當包含唯一的那組解。解是這樣表示的:輸出N個數字,分別表示A,B,C……所代表的數字,相鄰的兩個數字用一個空格隔開,不能有多余的空格。
輸入輸出樣例
輸入樣例#1: 復制5
ABCED
BDACE
EBBAA
輸出樣例#1: 復制
1 0 3 4 2
說明
對於30%的數據,保證有N<=10;
對於50%的數據,保證有N<=15;
對於全部的數據,保證有N<=26。
noip2004提高組第4題
思路:簡直太他媽的玄學了!!
兩個剪枝,倒著搜 50
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> usingnamespace std; int n,cnt; char a[27],b[27],c[27]; int s1[27],s2[27],s3[27]; int num[27],vis[27],net[27]; int jz(){ if(num[s1[0]]+num[s2[0]]>=n) return true; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if(A==-1||B==-1||C==-1) continue; if((A+B)%n!=C&&(A+B+1)%n!=C) return true; } return false; } int judge(){ int last=0; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if((A+B+last)%n!=C) return false; last=(A+B+last)/n; } if(last!=0) return false; return true; } void dfs(int now){ if(jz()==true) return ; if(now==n) if(judge()==true){ for(int i=0;i<n;i++) cout<<num[i]<<" "; exit(0); } for(int i=n-1;i>=0;i--) if(!vis[i]){ num[now]=i; vis[i]=1; dfs(now+1); num[now]=-1; vis[i]=0; } } int main(){ scanf("%d",&n); cin>>a>>b>>c; for(int i=0;i<n;i++){ s1[i]=a[i]-‘A‘; s2[i]=b[i]-‘A‘; s3[i]=c[i]-‘A‘; } memset(vis,0,sizeof(vis)); memset(num,-1,sizeof(num)); dfs(0); return 0; }
兩個剪枝,正著搜:70
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,cnt; char a[27],b[27],c[27]; int s1[27],s2[27],s3[27]; int num[27],vis[27],net[27]; int jz(){ if(num[s1[0]]+num[s2[0]]>=n) return true; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if(A==-1||B==-1||C==-1) continue; if((A+B)%n!=C&&(A+B+1)%n!=C) return true; } return false; } int judge(){ int last=0; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if((A+B+last)%n!=C) return false; last=(A+B+last)/n; } if(last!=0) return false; return true; } void dfs(int now){ if(jz()==true) return ; if(now==n) if(judge()==true){ for(int i=0;i<n;i++) cout<<num[i]<<" "; exit(0); } for(int i=0;i<n;i++) if(!vis[i]){ num[now]=i; vis[i]=1; dfs(now+1); num[now]=-1; vis[i]=0; } } int main(){ scanf("%d",&n); cin>>a>>b>>c; for(int i=0;i<n;i++){ s1[i]=a[i]-‘A‘; s2[i]=b[i]-‘A‘; s3[i]=c[i]-‘A‘; } memset(vis,0,sizeof(vis)); memset(num,-1,sizeof(num)); dfs(0); return 0; }
倒著搜+兩個剪枝+玄學剪枝
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,cnt; char a[27],b[27],c[27]; int s1[27],s2[27],s3[27]; int num[27],vis[27],net[27]; int jz(){ if(num[s1[0]]+num[s2[0]]>=n) return true; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if(A==-1||B==-1||C==-1) continue; if((A+B)%n!=C&&(A+B+1)%n!=C) return true; } return false; } int judge(){ int last=0; for(int i=n-1;i>=0;i--){ int A=num[s1[i]],B=num[s2[i]],C=num[s3[i]]; if((A+B+last)%n!=C) return false; last=(A+B+last)/n; } if(last!=0) return false; return true; } void dfs(int now){ if(jz()==true) return ; if(now==n) if(judge()==true){ for(int i=0;i<n;i++) cout<<num[i]<<" "; exit(0); } for(int i=n-1;i>=0;i--) if(!vis[i]){ num[net[now]]=i; vis[i]=1; dfs(now+1); num[net[now]]=-1; vis[i]=0; } } void getnet(int x){ if(vis[x]==0){ net[cnt++]=x; vis[x]=1; } return ; } int main(){ scanf("%d",&n); cin>>a>>b>>c; for(int i=0;i<n;i++){ s1[i]=a[i]-‘A‘; s2[i]=b[i]-‘A‘; s3[i]=c[i]-‘A‘; } for(int i=n-1;i>=0;i--){ getnet(s1[i]); getnet(s2[i]); getnet(s3[i]); } memset(vis,0,sizeof(vis)); memset(num,-1,sizeof(num)); dfs(0); return 0; }
洛谷 P1092 蟲食算