「JOISC2020」建筑装饰 4

传送门
(dp_{i, j, A/B}) 表示构造出前 (i) 位,选了 (j) 次 A,并且当前这一位是 A/B 的可行性。
可以发现 (dp_{i, j, A / B}) 为真的情况里,(j) 一定构成一段连续的区间。
那么我们只需要维护最小的和最大的 (j) 即可。
有解的条件就是看 (n) 在不在最后选 A/B 对应的 (j) 的区间内即可。
输出方案可以从后往前贪心输出。
参考代码:

#include <cstring>
#include <cstdio>

const int _ = 1e6 + 5;

void chkmin(int& a, int b) { a = a < b ? a : b; }
void chkmax(int& a, int b) { a = a > b ? a : b; }

int n, a[_], b[_], mn[2][_], mx[2][_], ans[_];

int chk(int l, int x, int r) { return l <= x && x <= r; }

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n << 1; ++i) scanf("%d", a + i);
    for (int i = 1; i <= n << 1; ++i) scanf("%d", b + i);
    for (int i = 1; i <= n << 1; ++i)
        mn[0][i] = mn[1][i] = 1e9, mx[0][i] = mx[1][i] = -1e9;
    for (int i = 1; i <= n << 1; ++i) {
        if (a[i] >= a[i - 1]) chkmin(mn[0][i], mn[0][i - 1] + 1), chkmax(mx[0][i], mx[0][i - 1] + 1);
        if (a[i] >= b[i - 1]) chkmin(mn[0][i], mn[1][i - 1] + 1), chkmax(mx[0][i], mx[1][i - 1] + 1);
        if (b[i] >= a[i - 1]) chkmin(mn[1][i], mn[0][i - 1]), chkmax(mx[1][i], mx[0][i - 1]);
        if (b[i] >= b[i - 1]) chkmin(mn[1][i], mn[1][i - 1]), chkmax(mx[1][i], mx[1][i - 1]);
    }
    if (!chk(mn[0][n << 1], n, mx[0][n << 1]) && !chk(mn[1][n << 1], n, mx[1][n << 1])) { puts("-1"); return 0; }
    int p = chk(mn[0][n << 1], n, mx[0][n << 1]);
    for (int i = n << 1, cnt = n; i; --i) {
        ans[i] = p, cnt -= p;
        if (a[i - 1] <= (p ? a[i] : b[i]) && chk(mn[0][i - 1], cnt, mx[0][i - 1])) p = 1; else p = 0;
    }
    for (int i = 1; i <= n << 1; ++i) putchar(ans[i] ? 'A' : 'B');
    return 0;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/12858902.html