1 条题解

  • 0
    @ 2026-10-27 5:41:08

    思路与算法

    从最低列向最高列处理加法,为每个字母保存尚未分配或对应数字,用位掩码记录已使用数字。先分配当前列两个加数字母,随后由它们与输入进位唯一确定结果数字以及输出进位;若结果字母已赋值必须相等,否则必须尚未使用。递归前可检查全体已经完全赋值的列:沿低位到高位维护仍可能的进位集合,未完全赋值的列保守地允许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

    信息

    ID
    2018
    时间
    1000ms
    内存
    64MiB
    难度
    提高
    标签
    递交数
    0
    已通过
    0
    上传者