首页 > 其他分享 >AT_abc326_d ABC Puzzle 题解

AT_abc326_d ABC Puzzle 题解

时间:2023-10-31 10:24:40浏览次数:44  
标签:字符 op2 op3 int 题解 Puzzle abc326 iscover ans

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;
}

标签:字符,op2,op3,int,题解,Puzzle,abc326,iscover,ans
From: https://www.cnblogs.com/RainPPR/p/solution-at-abc326-d.html

相关文章

  • AT_abc326_e Revenge of "The Salary of AtCoder Inc." 题解
    AT_abc326_eRevengeof"TheSalaryofAtCoderInc."题解一道简单的概率论+动态规划题目(然而我赛时没看这道题题意有一个长度为\(n\)的序列\(A\)、一个\(n\)面骰子,掷若干次骰子,如果这一次掷骰子的点数小于等于上一次的点数,则结束。定义这若干次掷骰子的总的结果为,每次......
  • AT_abc326_f Robot Rotation 题解
    AT_abc326_fRobotRotation题解经典问题,以前遇到过一个类似的问题:[ABC082D]FTRobot。建议对比着看一看这两道题,是两种不同的思路。(那一道题不用输出方案,因此可以用bitset优化;而此题需要输出方案,因此需要双向搜索。思路注意到每次只能「左转」和「左转」。所以,偶数次走......
  • AT_abc325_f Sensor Optimization Dilemma 题解
    AT_abc325_fSensorOptimizationDilemma题解Date20231025:修复手滑公式\(\min\)、\(\max\)写反了。动态规划。类似背包问题。朴素算法记\((x,y)\)表示使用\(x\)个(1)传感器、\(y\)个(2)号传感器。设\(f(t,i,j)\)表示覆盖前\(t\)个区间,使用\((i,j)\)传感......
  • AT_abc325_g offence 题解
    AT_abc325_goffence题解一道不难但是需要想一想的区间DP。有一个比较复杂的例子:ooofofxxx,简单的分析可知,一个of后面删除多少,与其前、后都有关,于是考虑区间DP。想到这里,其实问题已经解决一半了。状态设计设\(f(l,r)\)为闭区间\([l,r]\)经过操作之后的最小长度。注......
  • [CSP-S2020] 儒略日 题解
    [CSP-S2020]儒略日今儿终于做掉困扰多年的题目了,其实想好细节也不难。容易发现儒略历和格里高利历的润年判断方式不一样,并且中间有消失的十天,计算起来相当不方便。所以我们可以首先计算出\(-4713.1.1\)~\(1582.10.4\)会经过多少天,可以通过一天一天暴力跳的方法计算出需要\(......
  • P4309 [TJOI2013] 最长上升子序列题解
    P4309[TJOI2013]最长上升子序列题解正文单调队列?单调锤子队列!!本题的操作可以省略成:单点修改区间查询好极了,此时我们有两种选择:线段树和树状数组,(平衡树,真不会,下一位因为不需要其他操作,所以我们还是选择更小巧更可爱的树状数组吧。关于vectorvector的insert操作支......
  • Atcoder Beginner Contest 326 (ABC326)
    不知道为什么拖到现在,我是摆怪。A.2UP3DOWN模拟,略。B.326-likeNumbers模拟,略。C.Peak双指针板子。D.ABCPuzzle基础dfs。但是赛时不知道为什么觉得状态数不会很少,于是写了一个巨大复杂的状压。这里粗略算算有效状态数:仅考虑每行的限制,有\(\binom{3}{5}=10\)种选......
  • 题解 ABC326G【Unlock Achievement】
    题解ABC326G【UnlockAchievement】problem有\(n\)项属性,第\(j\)个属性的等级\(l_j\)初始为\(1\),每提升一级花费\(c_j\)的钱。又有\(m\)项成就,第\(i\)项成就要求对于所有\(1\leqj\leqn\),都要\(l_j\geqL_{i,j}\),如果满足所有要求,获得\(a_i\)的钱。求你最多......
  • ADASTRNG - Ada and Substring 题解
    ADASTRNG-AdaandSubstring题解题目描述给定一个小写字符串。输出\(26\)个数,代表以\(\texttt{a}\sim\texttt{z}\)开头的本质不同的子串个数。题目分析高度数组模板题。可以想到将以每个字符开头的本质不同的子串数目转化为:以每个字符开头的所有字串数目减去以每......
  • Luogu P4168 [Violet] 蒲公英 题解
    题目链接[Violet]蒲公英分析可以先将\(a[i]\)离散化然后考虑分块对于询问\(x,y\),\(x\)属于\(p\),\(y\)属于\(q\)当\(q-p<=1\)时直接暴力枚举即可,时间复杂度为\(O(\sqrt{n})\)\(else\)如图中间为分好块的地方我们发现,\(ans\)只可能为中间的众数或两边的......