题目描述
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
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(灯)