poj 3548 Restoring the digits
暴力+模拟
题意:一行字符串没有空格,是一个加法或减法算术表达式。数字中有大写字母,大写字母代表了某个数字,一个字母只能代表1种数字,不同字母代表的数字不同,另外允许存在前导0,另外字母总数最多10个。另外题目中说第1个数字要大于等于第2个数字,所以我做了判断,然后wa了一个早上+一个晚上,注释掉就AC了很无语。。。。
另外答案可能有多种,任意一种都可以的,至少我的代码是这样,并且保证是有解的。另外这题网上找不到题解的,确实我也觉得这题很无聊。。。
但是选拔赛的时候,居然想成了拓扑排序(因为做过类似的,又一次被驴舔了)
说说做法吧
因为字母最多十个,所以就暴力枚举,可想而知枚举量最大是10!,枚举后就把3个数字都先转化为int型,不要在数组里面模拟相加相减还进位什么的,这样子其实更容易错。如果一旦找到符合条件的就跳出了,否则会超时
另一个枚举是用二进制和next_permutation,最多是10个字母,所以最大状态是1023,转为二进制看有多少个1,但1的个数和字母个数相同就是我们要的,好像1010,有两个1,分别在3和1,所以我们就要3,1,分别对应两个字母的数值,然后3,1还不够我们还要枚举全排列才能考虑所有情况,接着后面的做法就是一样的了
然后看代码吧,代码是dfs版本和next_permutation版本的都有了,其实都差不多
dfs
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 20 #define MAX 3628810 char s[3][N],op; int n[3][N],len[3]; int num; struct cha { char c; int n; }a[N]; bool number[10]; bool OK; void init(char *tmp) { bool used[2*N]; memset(n,0,sizeof(n)); memset(s,0,sizeof(s)); memset(a,0,sizeof(a)); memset(used,0,sizeof(used)); num=0; int i,j,k; for(i=0,j=0; i<3; i++) { len[i]=j; for(k=0;;j++,k++) { if(tmp[j]=='+' || tmp[j]=='-' || tmp[j]=='=' || tmp[j]=='\0') break; s[i][k]=tmp[j]; if(tmp[j]>='0' && tmp[j]<='9') n[i][k]=tmp[j]-'0'; else { n[i][k]=tmp[j]-'A'+26; if(!used[tmp[j]-'A']) { used[tmp[j]-'A']=true; a[num++].c=tmp[j]; } } } s[i][k]='\0'; if(i==0) op=tmp[j]; len[i]=j-len[i]; j++; } } int tran(int i) { int ans=0; for(int k=0; k<len[i]; k++) ans=ans*10+n[i][k]; return ans; } void cal() { for(int i=0; i<3; i++) for(int j=0; j<len[i]; j++) if(s[i][j]>='A' && s[i][j]<='Z') for(int k=0; k<num; k++) if(s[i][j] == a[k].c) n[i][j]=a[k].n; int ss[3]; for(int i=0; i<3; i++) ss[i]=tran(i); if(op=='+' && ss[0]+ss[1]==ss[2]) OK=true; if(op=='-' && ss[0]-ss[1]==ss[2]) OK=true; } void dfs(int c) { if(c>=num) //枚举结束 { cal(); return ; } for(int i=0; i<10; i++) if(!number[i]) { number[i]=true; a[c].n=i; dfs(c+1); if(OK) return ; number[i]=false; } } int cmp(struct cha p ,struct cha q) { return p.c < q.c; } void solve() { OK=false; sort(a,a+num,cmp); memset(number,false,sizeof(number)); dfs(0); if(!OK) for(int i=0; i<num; i++) printf("%c %d\n",a[i].c,a[i].n); } int main() { char tmp[3*N]; while(scanf("%s",tmp)!=EOF) { init(tmp); solve(); } return 0; }
stl
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 20 #define MAX 3628810 char s[3][N],op; int n[3][N],len[3]; int num; struct cha { char c; int n; }a[N]; bool OK; void init(char *tmp) { bool used[2*N]; memset(n,0,sizeof(n)); memset(s,0,sizeof(s)); memset(a,0,sizeof(a)); memset(used,0,sizeof(used)); num=0; int i,j,k; for(i=0,j=0; i<3; i++) { len[i]=j; for(k=0;;j++,k++) { if(tmp[j]=='+' || tmp[j]=='-' || tmp[j]=='=' || tmp[j]=='\0') break; s[i][k]=tmp[j]; if(tmp[j]>='0' && tmp[j]<='9') n[i][k]=tmp[j]-'0'; else { n[i][k]=tmp[j]-'A'+26; if(!used[tmp[j]-'A']) { used[tmp[j]-'A']=true; a[num++].c=tmp[j]; } } } s[i][k]='\0'; if(i==0) op=tmp[j]; len[i]=j-len[i]; j++; } } int tran(int i) { int ans=0; for(int k=0; k<len[i]; k++) ans=ans*10+n[i][k]; return ans; } void solve(int *per) { sort(per,per+num); //全排列前记得排序 do //对per枚举全排列 { for(int i=0; i<num; i++) a[i].n=per[i]; for(int i=0; i<3; i++) for(int j=0; j<len[i]; j++) if(s[i][j]>='A' && s[i][j]<='Z') for(int k=0; k<num; k++) if(s[i][j] == a[k].c) n[i][j]=a[k].n; int ss[3]; for(int i=0; i<3; i++) ss[i]=tran(i); if(op=='+' && ss[0]+ss[1]==ss[2]) {OK=true; break;} if(op=='-' && ss[0]-ss[1]==ss[2]) {OK=true; break;} } while(next_permutation(per,per+num)); } int cmp(struct cha p ,struct cha q) { return p.c < q.c; } void BF() { int per[20]; OK=false; sort(a,a+num,cmp); for(int state=0; state<1024; state++) { int cc=0; for(int k=0; k<10; k++) if(state&(1<<k)) per[cc++]=k; if(cc==num) //1的个数和 solve(per); if(OK) break; } for(int i=0; i<num; i++) printf("%c %d\n",a[i].c,a[i].n); } int main() { char tmp[3*N]; while(scanf("%s",tmp)!=EOF) { init(tmp); BF(); } return 0; }