Page Top

AT_abc326_d ABC Puzzle 题解

AT_abc326_d ABC Puzzle 题解

看题

事实上,即使在 \(N=5\) 的情况下,也只有 \(66240\) 个网格满足「每行/每列恰好包含一个 ABC」。——官方题解

其实看到这道题,就感觉是搜索,这很显然。

但是我们会发现,最最最 native 的搜索,是 \(4^{5\times5}=2^{50}\) 的。

感觉不大可过,但是似乎又不太大。

考虑到原题中的限制很多很多,所以可以考虑剪枝。

下面的思路与 官方题解 的类似。

分析

题目中的限制有:

  1. 每行和每列恰好包含一个 A、一个 B 和一个 C
  2. \(x\) 行最左边的字符是 \(R_x\)
  3. \(x\) 列最上面的字符是 \(C_x\)

我们一条一条的看,可以怎么剪枝。

0x01

限制:每行和每列恰好包含一个 A、一个 B 和一个 C

于是,我们可以按行搜索,即每次递归填入一整行的字符。

然后我们在搜索的过程中记录:\(\mathit{have}_{i,0/1/2}\) 表示第 \(i\) 列是否有 A/B/C

然后在填入每一行的字符时,我们限制只能填入一个 A、一个 B 和一个 C

0x02

限制:每行的第一个字符一定是 \(R_x\)

于是,我们可以枚举每个字符填入的位置。

我们枚举 \(i\) 表示 \(R_x\) 填入的位置,然后枚举 \(j,k\) 表示其余两个字母的位置。

注意到除了后面两个字符的位置 \(j,k\)(在符合其余限制的情况下)可以调换:

\[\begin{array}{r} i\leftarrow[0,n)\\ j,k\leftarrow(i,n) \end{array} \]

0x03

限制:每一列第一个字符一定是 \(C_x\)

于是,我们可以记录一个 \(\mathit{cover}_i\) 表示此时第 \(i\) 列上方是否已经被其他字符覆盖。

当我们填入字符的时候,如果 \(\mathit{cover}_i=0\) 就需要判断这个字符是否等于 \(C_i\)

代码

评测记录:https://atcoder.jp/contests/abc326/submissions/47081201

跑的嘎嘎快(

#include <bits/stdc++.h>

using namespace std;

#define rep(i, n) for (int i = 0; i < (n); ++i)

using vi = vector<int>;
using vvi = vector<vi>;
using vb = vector<bool>;

int n; string r, c; 
vvi ans;

void print(vvi &x) {
    for (auto i : x) {
        for (auto j : i) j == -1 ? putchar('.') : putchar('A' + j);
        putchar('\n');
    }
}

#define td(a, b, c, _a, _b, _c) (a) = (_a), (b) = (_b), (c) = (_c)

void dfs(int x, vb iscover, vb have[3]) {
    if (x == n) {
        for (int sb = 0; sb < 3; ++sb)
            for (int t : have[sb]) if (!t) return;
        printf("Yes\n");
        print(ans), exit(0);
    } int op1 = r[x], op2, op3;
    if (op1 == 'A') op2 = 'B', op3 = 'C';
    else if (op1 == 'B') op2 = 'A', op3 = 'C';
    else op2 = 'A', op3 = 'B';
    int sb1 = op1 - 'A', sb2 = op2 - 'A', sb3 = op3 - 'A';
    int t1, t2, t3; for (int i = 0; i < n - 2; ++i) {
        if (!iscover[i] && c[i] != op1) continue;
        if (have[sb1][i]) continue;
        for (int j = i + 1; j < n; ++j) {
            if (!iscover[j] && c[j] != op2) continue;
            if (have[sb2][j]) continue;
            for (int k = i + 1; k < n; ++k) {
                if (j == k) continue;
                if (!iscover[k] && c[k] != op3) continue;
                if (have[sb3][k]) continue;
                td(t1, t2, t3, iscover[i], iscover[j], iscover[k]);
                iscover[i] = iscover[j] = iscover[k] = 1;
                ans[x][i] = sb1, ans[x][j] = sb2, ans[x][k] = sb3;
                have[sb1][i] = have[sb2][j] = have[sb3][k] = 1;
                // fprintf(stderr, "= JOIN (%d, %c) (%d, %c) (%d, %c)\n", i, op1, j, op2, k, op3);
                dfs(x + 1, iscover, have);
                // fprintf(stderr, "= THROW (%d, %c) (%d, %c) (%d, %c)\n", i, op1, j, op2, k, op3);
                td(iscover[i], iscover[j], iscover[k], t1, t2, t3);
                ans[x][i] = -1, ans[x][j] = -1, ans[x][k] = -1;
                have[sb1][i] = have[sb2][j] = have[sb3][k] = 0;
            }
        }
    }
}

signed main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    cin >> n >> r >> c;
    rep(_, n) ans.push_back(vi(n, -1));
    vb x = vb(n); vb t[3] = {x, x, x};
    dfs(0, x, t); printf("No\n");
    return 0;
}
posted @ 2023-10-31 10:10  RainPPR  阅读(99)  评论(0编辑  收藏  举报