1 条题解
-
0
思路与算法
从最低列向最高列处理加法,为每个字母保存尚未分配或对应数字,用位掩码记录已使用数字。先分配当前列两个加数字母,随后由它们与输入进位唯一确定结果数字以及输出进位;若结果字母已赋值必须相等,否则必须尚未使用。递归前可检查全体已经完全赋值的列:沿低位到高位维护仍可能的进位集合,未完全赋值的列保守地允许0或1,完全赋值的列按加法筛去不可能进位。最终最高位不能再溢出。源数据保证唯一解,找到后输出A到第N个字母的数字。
正确性说明
任意合法加法在每一列都满足加数、进位和结果的模N与整除关系,因此枚举未定加数字母并唯一确定结果不会漏掉合法映射。位掩码保证所有字母数字互不相同;同一列出现相同字母时复用已有赋值,没有重复自由变量。进位集合检查对未定列采用可能性超集,所以只会剪去已经能证明矛盾的状态,不会排除真解。逐列递归结束时每列加法和最高溢出条件都已验证,返回的全异映射必为合法解;结合源唯一性保证得到所求唯一结果。
复杂度
初始化字母数组与读入为O(N)。最坏枚举N!种数字映射,逐次可行性检查O(N),保守上界O(N²·N!);实际由逐列决定结果、进位和全异剪枝大幅减少。递归深度和状态保存为O(N),额外空间O(N),没有将测试点规模缩成低进制。
易错点
允许前导零,不能自行禁止。字母顺序不等于数字顺序,同一字母可能在一列重复出现。必须拒绝最高位溢出;恢复现场时只撤销本层真正新分配的字母。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int n, a[3][26], val[26]; unsigned used = 0; bool feasible() { int possible = 1; for (int col = n - 1; col >= 0; col--) { int x = val[a[0][col]], y = val[a[1][col]], z = val[a[2][col]], next = 0; if (x < 0 || y < 0 || z < 0) { possible = 3; continue; } for (int carry = 0; carry <= 1; carry++) if ((possible >> carry) & 1) { int sum = x + y + carry; if (sum % n == z) next |= 1 << (sum / n); } possible = next; if (!possible) return false; } return possible & 1; } bool dfs(int col, int carry) { if (col < 0) return carry == 0; int x = a[0][col], y = a[1][col], z = a[2][col]; if (val[x] < 0) { for (int d = n - 1; d >= 0; d--) if (!(used >> d & 1)) { val[x] = d; used |= 1u << d; if (feasible() && dfs(col, carry)) return true; used ^= 1u << d; val[x] = -1; } return false; } if (val[y] < 0) { for (int d = n - 1; d >= 0; d--) if (!(used >> d & 1)) { val[y] = d; used |= 1u << d; if (feasible() && dfs(col, carry)) return true; used ^= 1u << d; val[y] = -1; } return false; } int sum = val[x] + val[y] + carry, d = sum % n; if (val[z] >= 0) return val[z] == d && dfs(col - 1, sum / n); if (used >> d & 1) return false; val[z] = d; used |= 1u << d; if (feasible() && dfs(col - 1, sum / n)) return true; used ^= 1u << d; val[z] = -1; return false; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); if (!(cin >> n)) return 0; for (int i = 0; i < 3; i++) { string s; cin >> s; for (int j = 0; j < n; j++) a[i][j] = s[j] - 'A'; } fill(val, val + 26, -1); if (dfs(n - 1, 0)) { for (int i = 0; i < n; i++) { if (i) cout << ' '; cout << val[i]; } cout << '\n'; } }
- 1