CF10D LCIS

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

是远古的 CF 题一枚呀。

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

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

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

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

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

f(i,j)={f(i1,j)A[i]B[j]max0k<j,B[k]<B[j]{f(i1,k)}+1A[i]=B[j]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)=0f(i, 0) = f(0, j) = 0

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

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

S(i,j+1)={S(i,j)B[j]A[i]S(i,j){j}B[j]<A[i]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}

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

g(i,j+1)={g(i,j)B[j]A[i]\orf(i1,j)g(i,j)f(i1,j)B[j]<A[i]\andf(i1,j)>g(i,j)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}

用一个变量滚动地维护 gg 即可。

记录方案直接 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;
}
posted @   dbxxx  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示