C - 基爷与加法等式
Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
一天,上小学的妹妹跑过来问基爷一道字母加法等式,基爷不假思索的便给出了一组可行解。
聪明的你发现,一个字母等式可能有多种不同解,于是你想编个程序计算一下
Input
输入包含多组数据。
每组数据第一行一个整数n
,表示有n个字符串 3 ≤ n ≤ 10
接下来n行,每行有1个最多只含10个大写字母的字符串,前 n - 1 行的字符串表示加数,第 n 行的字符串表示和
每个样例最多只有10个互不相同的大写字母,每个字母表示 0 - 9
中的一个数,相同字母表示相同的数,不同字母表示不同的数
Output
对于每组数据输出一个整数,表示不同的可行解的数量。
对于两个可行解,只要有一个字母表示不同的数字,我们就认为这两个可行解不同
Sample input and output
Sample Input | Sample Output |
---|---|
4 TAI SHEN LA ACER 3 SEND MORE MONEY |
76 1 |
Hint
如果各个字符串长度不等,右对齐后再运算
每个字符串最左边的字母表示的数字不能为0
不保证最后一个字符串的长度大于等于前面的表示加数的字符串长度
解题报告:
这是一道回溯法题目.
首先回溯法和生成->检查法的区别?
回溯法是指在搜索中遇到不可能的情况就直接回溯(提高效率)
而生成->检查法只会在最后检查是否合法(效率较低)
本题就需要使用回溯法这一利器.
首先题目要求右对齐,我们将读入的字符串全部反转即可.
之后用一个map<char,int>记录这个字符代表的含义,然后按照 列推进 的方式推进到最后一列(每一列从上到下依次访问各行),当我们每次到达最后一行的时候,都要检查是否合法,若不合法,直接回溯并恢复现场.
<这里的加法类似于 全加器 ,我们设计dfs的时候需要多一个前面的进位信号>
同时注意长度问题,其他就没什么了
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <map> using namespace std; typedef int status[9]; const int maxn = 15; const int MaxStatusSize = 1500000; const int MaxHashSize = 1403641; int n,len[maxn],maxlen,fac[maxn],size,head[MaxHashSize],new_next[MaxHashSize]; long long ans; char s[maxn][maxn]; map<char,int>m; bool used[maxn]; status st[MaxStatusSize]; int gethashvalue(const status &x) { int res = 0; for(int i = 0 ; i < 10 ; ++ i) { int cot = 0; for(int j = i+1 ; j < 10 ; ++ j) if (x[i] > x[j]) cot++; res += fac[8-i]*cot; } return res; } void init_hash() { memset(head,-1,sizeof(head)); } bool insert(int id) { int val = gethashvalue(st[id]); int u = head[val]; while(u != -1) { if (!memcmp(&st[id],&st[u],sizeof(int)*9)) return false; u = new_next[u]; } new_next[id] = head[val]; head[val] = id; return true; } //推进到了第 cur 列,目前正在第 cur 列的第 pos 行,来自前面的进位信号是 c void dfs(int cur,int pos,int c) { if (cur == maxlen) { if (c) return; if (!c) { map<char,int>::iterator it = m.begin(); int pos = 0; while(it != m.end()) st[size][pos++] = (it++)->second; if(insert(size)) { ans++; size++; } } } else { if (pos == n-1) { int val = m[s[pos][cur]]; if (val == -1) //该数还没有定 { int num = c % 10; int next = (c-num) / 10; //进位数 if (used[num]) //已被使用 return; if (!num && s[pos][cur] == s[pos][len[pos]-1]) return; m[s[pos][cur]] = num; //设置 used[num] = true; dfs(cur+1,0,next); m[s[pos][cur]] = -1; used[num] = false; } else { int num = c % 10; if (val != num) return; if (s[pos][cur] == s[pos][len[pos]-1] && !num) //首位不能为0 return; int next = (c-num)/10; dfs(cur+1,0,next); } } else { if (cur >= len[pos]) //没有位置 dfs(cur,pos+1,c); else { int val = m[s[pos][cur]]; if (val != -1) { if (val == 0 && s[pos][cur] == s[pos][len[pos]-1]) return; dfs(cur,pos+1,c+val); } else { int st = 0; if (s[pos][cur] == s[pos][len[pos]-1]) // 首位不允许为0 st = 1; for(int i = st ; i <= 9 ; ++ i) if (!used[i]) { used[i] = true; m[s[pos][cur]] = i; dfs(cur,pos+1,c+i); used[i] = false; m[s[pos][cur]] = -1; } } } } } } int main(int argc,char *argv[]) { fac[0] = 1; for(int i = 1 ; i <= 10 ; ++ i) fac[i] = i*fac[i-1]; while(~scanf("%d",&n)) { memset(used,false,sizeof(used));m.clear();ans=0;size=0;init_hash(); int thismax = 0; for(int i = 0 ; i < n ; ++ i) { scanf("%s",s[i]); len[i] = strlen(s[i]); thismax = max(thismax,len[i]); reverse(s[i],s[i]+len[i]); for(int j = 0 ; j < len[i] ; ++ j) m[s[i][j]] = -1; } if (len[n-1] < thismax) { printf("0 "); continue; } maxlen = thismax; dfs(0,0,0); printf("%lld ",ans); } return 0; }