CF10D LCIS – 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

是远古的 CF 题一枚呀。

不难想到状态,\(f(i, j)\) 表示 \(A[1, i]\)\(B[1, j]\) 内可构成的 LCIS 长度。

然后会发现,保证上升的这一部分转移是比较难以进行的,因为我们想得到单调性,却没有一个明确的比较目标。

回顾 LIS 的做法,\(f(i)\) 表示 \(A[1, i]\) 且以 \(A[i]\) 结尾的 LIS 长度。

那么修改状态,\(f(i, j)\) 表示 \(A[1, i]\)\(B[1, j]\) 内,且以 \(B[j]\) 结尾的 LCIS 长度。

这样转移方程就很好写了,我们令 \(A[0] = B[0] = -\infty\)

\[f(i, j) = \begin{cases} f(i – 1, j) & A[i] \ne B[j] \\ \max\limits_{0\le k < j, B[k] < B[j]}\{f(i – 1, k)\} + 1 & A[i] = B[j] \end{cases} \]

边界:\(f(i, 0) = f(0, j) = 0\)

容易看出,直接这么写是 \(\mathcal{O}(nm^2)\) 的。可能已经足以通过本题?不过事实上还有一种 \(\mathcal{O}(nm)\) 的做法。

发现瓶颈在于 \(k\) 的循环枚举,事实上这是可以优化的。我们发现在上面转移方程的第二种情况中,\(B[k] < B[j]\) 等价于 \(B[k] < A[i]\),也就是这个条件的是否成立和 \(j\) 的取值无关。我们记满足 \(0 \le k < j, B[k] < A[i]\) 的所有 \(k\) 构成的集合记为 \(S(i, j)\)。发现有:

\[S(i, j +1) = \begin{cases} S(i, j) & B[j] \ge A[i]\\ S(i, j) \cup \{j\} & B[j] < A[i] \end{cases} \]

因此枚举 \(j\) 时同时维护 \(S\) 中所有 \(k\) 对应的 \(f(i – 1, k)\) 的最大值即可。具体来说,设 \(g(i, j)\)\(\max \limits _{k\in S(i, j)}\{f(i-1, k)\}\),则有:

\[g(i, j +1) = \begin{cases} g(i, j) & B[j] \ge A[i] \or f(i – 1, j) \le g(i, j)\\ f(i – 1, j) & B[j] < A[i] \and f(i – 1, j) > g(i, j) \end{cases} \]

用一个变量滚动地维护 \(g\) 即可。

记录方案直接 dp 时记录前驱。

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-11-06 00:59:09 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-11-06 03:02:53
 */
#include <bits/stdc++.h>
inline int read() {
    int x = 0;
    bool flag = true;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            flag = false;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    if(flag)
        return x;
    return ~(x - 1);
}

const int maxn = 505;
const int maxm = 505;

int a[maxn], b[maxm];
int f[maxn][maxm];
int pre[maxn][maxm];

void pr(int x, int n) {
    if (x == 0)
        return ;
    pr(pre[n][x], n);
    printf("%d ", b[x]);
}

int main() {
    int n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    int m = read();
    for (int i = 1; i <= m; ++i)
        b[i] = read();
    
    a[0] = b[0] = -1;

    for (int i = 1; i <= n; ++i) {
        int k = 0;
        for (int j = 1; j <= m; ++j) {
            if (a[i] != b[j]) {
                f[i][j] = f[i - 1][j];
                pre[i][j] = pre[i - 1][j];
            } else {
                f[i][j] = f[i - 1][k] + 1;
                pre[i][j] = k;
            }

            if (b[j] < a[i] && f[i - 1][j] > f[i - 1][k])
                k = j;
        }
    }

    int st = 0;
    for (int i = 1; i <= m; ++i)
        if (f[n][i] > f[n][st])
            st = i;

    printf("%d\n", f[n][st]);
    pr(st, n);
    puts("");
    return 0;
}

原文地址:http://www.cnblogs.com/crab-in-the-northeast/p/cf10d.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性