蟲食算&燈
題目描述
所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看一個簡單的例子:
43#9865#045
+ 8468#6633
44445509678
其中$#$號代表被蟲子啃掉的數字。根據算式,我們很容易判斷:第一行的兩個數字分別是55和33,第二行的數字是55。
現在,我們對問題做兩個限制:
首先,我們只考慮加法的蟲食算。這裡的加法是NN進位制加法,算式中三個數都有NN位,允許有前導的00。
其次,蟲子把所有的數都啃光了,我們只知道哪些數字是相同的,我們將相同的數字用相同的字母表示,不同的數字用不同的字母表示。如果這個算式是NN進位制的,我們就取英文字母表午的前NN個大寫字母來表示這個算式中的00到N-1N−1這NN個不同的數字:但是這NN個字母並不一定順序地代表00到N-1N−1。輸入資料保證NN個字母分別至少出現一次。
BADC
+CBDA
DCCC
上面的算式是一個4進位制的算式。很顯然,我們只要讓ABCDABCD分別代表01230123,便可以讓這個式子成立了。你的任務是,對於給定的NN進位制加法算式,求出NN個不同的字母分別代表的數字,使得該加法算式成立。輸入資料保證有且僅有一組解。
輸入輸出格式
輸入格式:
包含四行。
第一行有一個正整數N(N \le 26)N(N≤26)。
後面的三行,每行有一個由大寫字母組成的字串,分別代表兩個加數以及和。這3個字串左右兩端都沒有空格,從高位到低位,並且恰好有NN位。
輸出格式:
一行,即唯一的那組解。
解是這樣表示的:輸出NN個數字,分別表示A,B,C,…A,B,C,…所代表的數字,相鄰的兩個數字用一個空格隔開,不能有多餘的空格。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int maxn=307; 8 int a[maxn],b[maxn],c[maxn],num[maxn],n;9 bool vis[maxn]; 10 void dfs(int pos,int lft){ 11 //cout<<pos<<" "<<lft<<":"<<endl; 12 //for(int i=1;i<=n;i++) cout<<num[i]<<" "; cout<<endl; 13 14 15 if(pos==n+1&&lft!=0) return; 16 /*for(int i=pos+1;i<=n;i++){ 17 if(!vis[num[a[pos]]]||!vis[num[b[pos]]]||!vis[num[c[pos]]]) continue; 18 if((num[a[pos]]+num[b[pos]])%n!=num[c[pos]]) return; 19 }*/ 20 bool flag=true; 21 for(int i=1;i<=n;i++) if(num[i]==-1) flag=false; 22 if(flag){ 23 for(int i=1;i<=n;i++) cout<<num[i]<<" "; cout<<endl; 24 exit(0); 25 } 26 27 28 if(num[a[pos]]>=0&&num[b[pos]]>=0&&num[c[pos]]>=0){ 29 //cout<<"a"<<endl; 30 if((num[a[pos]]+num[b[pos]]+lft)%n!=num[c[pos]]) return; 31 else dfs(pos+1,(num[a[pos]]+num[b[pos]+lft])/n); 32 } 33 34 else if(num[a[pos]]>=0&&num[b[pos]]>=0&&num[c[pos]]==-1){ 35 //cout<<"b"<<endl; 36 num[c[pos]]=(num[a[pos]]+num[b[pos]]+lft)%n; 37 if(vis[num[c[pos]]]){num[c[pos]]=-1;return;} 38 vis[num[c[pos]]]=true; 39 dfs(pos+1,(lft+num[a[pos]]+num[b[pos]])/n); 40 vis[num[c[pos]]]=false;num[c[pos]]=-1; 41 } 42 else if(num[a[pos]]>=0&&num[b[pos]]==-1&&num[c[pos]]>=0){ 43 //cout<<"c"<<endl; 44 for(int i=0;i<=n-1;i++){ 45 if(vis[i]) continue; 46 if((num[a[pos]]+i+lft)%n==num[c[pos]]){ 47 num[b[pos]]=i;vis[num[b[pos]]]=true; 48 dfs(pos+1,(lft+num[a[pos]]+num[b[pos]])/n); 49 vis[num[b[pos]]]=false;num[b[pos]]=-1; 50 } 51 } 52 } 53 else if(num[a[pos]]==-1&&num[b[pos]]>=0&&num[c[pos]]>=0){ 54 //cout<<"d"<<endl; 55 for(int i=0;i<=n-1;i++){ 56 if(vis[i]) continue; 57 if((i+num[b[pos]]+lft)%n==num[c[pos]]){ 58 num[a[pos]]=i;vis[num[a[pos]]]=true; 59 dfs(pos+1,(num[a[pos]]+num[b[pos]]+lft)/n); 60 vis[a[pos]]=false;num[a[pos]]=-1; 61 } 62 } 63 } 64 65 else if(num[a[pos]]==-1&&num[b[pos]]==-1&&num[c[pos]]>=0){ 66 //cout<<"e"<<endl; 67 for(int i=0;i<=n-1;i++){ 68 if(vis[i]) continue; 69 for(int j=0;j<=n-1;j++){ 70 if(vis[i]||vis[j]) continue; 71 if(a[pos]==b[pos]&&i!=j) continue; 72 if(a[pos]!=b[pos]&&i==j) continue; 73 if((i+j+lft)%n==num[c[pos]]){ 74 num[a[pos]]=i;num[b[pos]]=j;vis[num[a[pos]]]=true;vis[num[b[pos]]]=true; 75 dfs(pos+1,(num[a[pos]]+num[b[pos]]+lft)/n); 76 vis[num[a[pos]]]=false;vis[num[b[pos]]]=false;num[a[pos]]=-1;num[b[pos]]=-1; 77 } 78 } 79 } 80 } 81 else if(num[a[pos]]==-1&&num[b[pos]]>=0&&num[c[pos]]==-1){ 82 //cout<<"f"<<endl; 83 for(int i=0;i<=n-1;i++){ 84 if(vis[i]) continue; 85 for(int j=0;j<=n-1;j++){ 86 if(vis[i]||vis[j]) continue; 87 if(a[pos]==c[pos]&&i!=j) continue; 88 if(a[pos]!=c[pos]&&i==j) continue; 89 if((i+num[b[pos]]+lft)%n==j){ 90 num[a[pos]]=i;num[c[pos]]=j;vis[num[a[pos]]]=true;vis[num[c[pos]]]=true; 91 dfs(pos+1,(num[a[pos]]+num[b[pos]]+lft)/n); 92 vis[num[a[pos]]]=false;vis[num[c[pos]]]=false;num[a[pos]]=-1;num[c[pos]]=-1; 93 } 94 } 95 } 96 } 97 else if(num[a[pos]]>=0&&num[b[pos]]==-1&&num[c[pos]]==-1){ 98 //cout<<"g"<<endl; 99 for(int i=0;i<=n-1;i++){ 100 if(vis[i]) continue; 101 for(int j=0;j<=n-1;j++){ 102 if(vis[i]||vis[j]) continue; 103 if(b[pos]==c[pos]&&i!=j) continue; 104 if(b[pos]!=c[pos]&&i==j) continue; 105 if((num[a[pos]]+i+lft)%n==j){ 106 num[b[pos]]=i;num[c[pos]]=j;vis[num[b[pos]]]=true;vis[num[c[pos]]]=true; 107 dfs(pos+1,(num[a[pos]]+num[b[pos]]+lft)/n); 108 vis[num[b[pos]]]=false;vis[num[c[pos]]]=false;num[b[pos]]=-1;num[c[pos]]=-1; 109 } 110 } 111 } 112 } 113 114 else if(num[a[pos]]==-1&&num[b[pos]]==-1&&num[c[pos]]==-1){ 115 //cout<<"h"<<endl; 116 for(int i=0;i<=n-1;i++){ 117 if(vis[i]) continue; 118 for(int j=0;j<=n-1;j++){ 119 if(vis[i]||vis[j]) continue; 120 for(int k=0;k<=n-1;k++){ 121 if(vis[i]||vis[j]||vis[k]) continue; 122 if(a[pos]!=b[pos]&&i==j) continue; 123 if(a[pos]==b[pos]&&i!=j) continue; 124 if(a[pos]!=c[pos]&&i==k) continue; 125 if(a[pos]==c[pos]&&i!=k) continue; 126 if(b[pos]!=c[pos]&&j==k) continue; 127 if(b[pos]==c[pos]&&j!=k) continue; 128 if((i+j+lft)%n==k){ 129 num[a[pos]]=i;num[b[pos]]=j;num[c[pos]]=k;vis[num[a[pos]]]=true;vis[num[b[pos]]]=true;vis[num[c[pos]]]=true; 130 dfs(pos+1,(num[a[pos]]+num[b[pos]]+lft)/n); 131 vis[num[a[pos]]]=false;vis[num[b[pos]]]=false;vis[num[c[pos]]]=false;num[a[pos]]=-1;num[b[pos]]=-1;num[c[pos]]=-1; 132 } 133 } 134 } 135 } 136 } 137 } 138 int main(){ 139 freopen("a.in","r",stdin); 140 cin>>n; 141 for(int i=n;i>=1;i--){char t;cin>>t;a[i]=t-'A'+1;} 142 for(int i=n;i>=1;i--){char t;cin>>t;b[i]=t-'A'+1;} 143 for(int i=n;i>=1;i--){char t;cin>>t;c[i]=t-'A'+1;} 144 for(int i=1;i<=107;i++) num[i]=-1; 145 dfs(1,0); 146 }
沒有調出來的程式碼,沒法調了TAT
題解,是把多維的情況轉為一維,就是把要求量放入一個數組中,逐個求解
這樣可以降低程式碼複雜度,更容易接近高分,並且很好想
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int maxn=37; 8 int n,stp; 9 int a[maxn],b[maxn],c[maxn],num[maxn]; 10 bool vis[maxn],ins[maxn]; 11 int s[maxn]; 12 bool pan(){ 13 for(int i=1;i<=n;i++){ 14 int aa=num[a[i]];int bb=num[b[i]];int cc=num[c[i]]; 15 if(aa==-1||bb==-1||cc==-1) continue; 16 if((aa+bb)%n!=cc&&(aa+bb+1)%n!=cc) return true; 17 } 18 return false; 19 } 20 bool check(){ 21 for(int i=1,x=0;i<=n;i++){ 22 int A=num[a[i]],B=num[b[i]],C=num[c[i]]; 23 if(((A+B+x)%n)!=C) return false; 24 x=(A+B+x)/n; 25 } 26 return true; 27 } 28 void dfs(int x){ 29 //cout<<x<<endl; 30 if(pan()==true) return; 31 if(x==n+1) { 32 if(check()==true){ 33 for(int i=1;i<=n;i++) cout<<num[i]<<" "; cout<<endl; 34 exit(0); 35 } 36 return; 37 } 38 for(int i=n-1;i>=0;i--) 39 if(!vis[i]) { 40 num[s[x]]=i;vis[i]=true; 41 dfs(x+1); 42 num[s[x]]=-1; 43 vis[i]=false; 44 } 45 return; 46 } 47 int main(){ 48 //freopen("a.in","r",stdin); 49 cin>>n; 50 for(int i=0;i<=n;i++) num[i]=-1; 51 for(int i=n;i>=1;i--){ 52 char t;cin>>t; 53 a[i]=t-'A'+1; 54 } 55 for(int i=n;i>=1;i--){ 56 char t;cin>>t; 57 b[i]=t-'A'+1; 58 } 59 for(int i=n;i>=1;i--){ 60 char t;cin>>t; 61 c[i]=t-'A'+1; 62 } 63 for(int i=1;i<=n;i++){//低位先進 64 if(!ins[a[i]]){ 65 ins[a[i]]=true;s[++stp]=a[i]; 66 } 67 if(!ins[b[i]]){ 68 ins[b[i]]=true;s[++stp]=b[i]; 69 } 70 if(!ins[c[i]]){ 71 ins[c[i]]=true;s[++stp]=c[i]; 72 } 73 } 74 dfs(1); 75 }
這還在燈中有體現
貝希和她的閨密們在她們的牛棚中玩遊戲。但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了。貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望。她希望您能夠幫幫她,把所有的燈都給重新開起來!她才能繼續快樂地跟她的閨密們繼續玩遊戲! 牛棚中一共有N(1 <= N <= 35)盞燈,編號為1到N。這些燈被置於一個非常複雜的網路之中。有M(1 <= M <= 595)條很神奇的無向邊,每條邊連線兩盞燈。 每盞燈上面都帶有一個開關。當按下某一盞燈的開關的時候,這盞燈本身,還有所有有邊連向這盞燈的燈的狀態都會被改變。狀態改變指的是:當一盞燈是開著的時候,這盞燈被關掉;當一盞燈是關著的時候,這盞燈被開啟。 問最少要按下多少個開關,才能把所有的燈都給重新開啟。 資料保證至少有一種按開關的方案,使得所有的燈都被重新開啟。
這道題如果直接鄰接表加邊,用一個long long存狀態的話,會很複雜
程式碼根本調不出來
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=507; 9 int n,m,num,ans=0x7f7f7f7f; 10 ll tmp; 11 int head[maxn]; 12 struct Edge{ 13 int next,to,dis; 14 }edge[maxn]; 15 void add(int from,int to){ 16 edge[++num].next=head[from]; 17 edge[num].to=to; 18 head[from]=num; 19 } 20 void dfs(int x,int pre,bool flag,int stp){ 21 if(stp>=ans) return; 22 if(flag==false){ 23 bool all=true; 24 for(int i=1;i<=n;i++) if(!((tmp>>i)&1)) {all=false;break;} 25 if(all==true) { 26 ans=min(ans,stp); 27 return; 28 } 29 } 30 if(flag==true){ 31 for(int i=head[x];i;i=edge[i].next){ 32 int v=edge[i].to; 33 tmp^=(1<<v); 34 } 35 for(int i=head[x];i;i=edge[i].next){ 36 int v=edge[i].to;if(v==pre) continue; 37 dfs(v,x,0,stp);dfs(v,x,1,stp+1); 38 } 39 for(int i=head[x];i;i=edge[i].next){ 40 int v=edge[i].to; 41 tmp^=(1<<v); 42 } 43 } 44 else{ 45 for(int i=head[x];i;i=edge[i].next){ 46 int v=edge[i].to;if(v==pre) continue; 47 dfs(v,x,0,stp);dfs(v,x,1,stp+1); 48 } 49 } 50 } 51 int main(){ 52 freopen("a.in","r",stdin); 53 cin>>n>>m; 54 for(int i=1;i<=m;i++){ 55 int u,v;cin>>u>>v; 56 add(u,v);add(v,u); 57 } 58 for(int i=1;i<=n;i++) {tmp^=(1<<i);dfs(i,0,1,1);tmp^=(1<<i);} 59 cout<<ans<<endl; 60 return 0; 61 }
但如果把每個燈都設成一個數,
那麼如果兩個燈有邊連線,就是這個燈的數要加上與它有邊連線的燈的數,這樣如果這個燈選,那麼直接當前狀態異或一下這個燈的數,相當於把這些燈都調換了一下
再將燈從小到大列舉一遍搜尋動還是不動
當然這樣的前提是,每個燈最多隻會被動一遍,因為動兩邊相當於沒動,
這個動一遍的問題,顏鴻宇在夏令營的時候講過
這也說明,我看到問題,沒有有意識的想它的性質
這個一定要有,之前的區間統計,就沒有想到,不可能有 x,a,y,b的情況,如果想到了,那麼正解起碼要好想一點,還有之前郵票面值設計,搜尋的上下界就沒有想到,
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<map> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=607; 10 const int INF=0x7f7f7f7f; 11 int n,m,num,ans=INF; 12 ll all, bin[maxn],p[maxn]; 13 bool flag; 14 map<ll,int>mp; 15 void dfs(int x,ll tmp,int usd){//直接用x來記錄步數,usd記錄用了幾個 16 if(x==num+1){ 17 if(tmp==all) ans=min(ans,usd); 18 if(!flag){ 19 if(!mp[tmp]||mp[tmp]>usd) mp[tmp]=usd;//更新注意 20 return; 21 } 22 if(flag){ 23 if(mp[all-tmp]!=0){ 24 ans=min(ans,usd+mp[all-tmp]); 25 } 26 return; 27 } 28 return; 29 } 30 dfs(x+1,tmp,usd); 31 dfs(x+1,tmp^p[x],usd+1);//是異或不是+ 32 } 33 int main(){ 34 cin>>n>>m; 35 bin[1]=1;for(int i=2;i<=n+1;i++) bin[i]=bin[i-1]<<1; 36 all=bin[n+1]-1; 37 for(int i=1;i<=m;i++){ 38 int a,b;cin>>a>>b; 39 p[a]+=bin[b];p[b]+=bin[a];//使程式碼,思路簡化 40 } 41 for(int i=1;i<=n;i++) p[i]+=bin[i]; 42 num=n/2;dfs(1,0,0); 43 flag=true; 44 num=n;dfs(n/2+1,0,0); 45 cout<<ans<<endl; 46 return 0; 47 }
尼克的任務,郵票面值設計都有這種把最暴力無腦的搜尋或者列舉變得簡單好寫,或者降低了時間複雜度,空間複雜度
蟲食算和燈都有高斯消去元的解法
一定要完全掌握,但鑑於今天只做了兩個題
先把連結放在這,一定要看啊
https://www.luogu.org/problemnew/solution/P1092(蟲食算)
http://hzwer.com/4580.html(燈)
https://www.luogu.org/problemnew/solution/P2962(燈)