【ACWing】272. 最长公共上升子序列

题目地址:

https://www.acwing.com/problem/content/274/

给定两个长度都是 n n n的序列 A A A B B B,求其最长公共上升子序列的长度。

输入格式:
第一行包含一个整数 n n n,表示数列 A A A B B B的长度。第二行包含 n n n个整数,表示数列 A A A。第三行包含 n n n个整数,表示数列 B B B

输出格式:
输出一个整数,表示最长公共上升子序列的长度。

数据范围:
1 ≤ n ≤ 3000 1≤n≤3000 1n3000,序列中的数字均不超过 2 31 − 1 2^{31}−1 2311

思路是动态规划。设 f [ i ] [ j ] f[i][j] f[i][j] A A A的长 i i i的前缀和 B B B的长 j j j的前缀里的最长公共上升子序列的长度。我们可以分为两种情况,一种是含 A A A的第 i i i个数的,一种是不含(这里我们把 A A A的第 i i i个数直接写为 A [ i ] A[i] A[i]了)。不含 A [ i ] A[i] A[i]的最长公共上升子序列,实际就是 A A A的长 i − 1 i-1 i1的前缀和 B B B的长 j j j的前缀里的最长公共上升子序列,那么长度就是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j];对于含 A [ i ] A[i] A[i]的,我们并不能知道 A [ i ] A[i] A[i]是否能接到某个长度为 f [ i ] [ k ] , k < j f[i][k],k<j f[i][k],k<j的后面,所以要修改一下状态的定义。我们可以重新定义 f [ i ] [ j ] f[i][j] f[i][j] A A A的长 i i i的前缀和 B B B的长 j j j的前缀里的,并且以 B [ j ] B[j] B[j]结尾的最长公共上升子序列的长度,那么最终答案就是要求 max ⁡ 1 ≤ j ≤ n f [ n ] [ j ] \max _{1\le j\le n}f[n][j] max1jnf[n][j](当然也有可能无法包含 B B B的任意的字符,此时不存在公共子序列,答案是 0 0 0)。考虑 f [ i ] [ j ] f[i][j] f[i][j],对于不含 A [ i ] A[i] A[i]的最长公共上升子序列,答案仍然是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j];对于含 A [ i ] A[i] A[i]的最长公共上升子序列,含 A [ i ] A[i] A[i]当然意味着以 A [ i ] A[i] A[i]结尾,而 f [ i ] [ j ] f[i][j] f[i][j]的定义里又要求以 B [ j ] B[j] B[j]结尾,所以此时要求 A [ i ] = B [ j ] A[i]=B[j] A[i]=B[j],并且有 f [ i ] [ j ] = 1 + max ⁡ k < j ∧ B [ k ] < B [ j ] = A [ i ] f [ i − 1 ] [ k ] f[i][j]=1+\max_{k<j\land B[k]<B[j]=A[i]}f[i-1][k] f[i][j]=1+maxk<jB[k]<B[j]=A[i]f[i1][k]。综上,有: f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] , 1 + max ⁡ k < j ∧ B [ k ] < A [ i ] f [ i − 1 ] [ k ] } f[i][j]=\max\{f[i-1][j],1+\max_{k<j\land B[k]<A[i]}f[i-1][k]\} f[i][j]=max{ f[i1][j],1+k<jB[k]<A[i]maxf[i1][k]}在从 1 1 1 n n n遍历 j j j的时候,我们可以用一个变量来存一下 max ⁡ k < j ∧ B [ k ] < A [ i ] f [ i − 1 ] [ k ] \max_{k<j\land B[k]<A[i]}f[i-1][k] maxk<jB[k]<A[i]f[i1][k],这样就不用再来一层循环找最大值了。代码如下:

#include <iostream>
using namespace std;

const int N = 3010;
int n;
int a[N], b[N];
int f[N][N];

int main() {
    
    
    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);

    for (int i = 1; i <= n; i++) {
    
    
    	// 维护maxv是满足b[k] < a[i]的情况下,最大的f[i - 1][k]
        int maxv = 0;
        for (int j = 1; j <= n; j++) {
    
    
        	// 枚举不含a[i]的
            f[i][j] = f[i - 1][j];
            // 枚举含a[i]的
            if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv + 1);
            if (b[j] < a[i]) maxv = max(maxv, f[i - 1][j]);
        }
    }

    int res = 0;
    for (int i = 1; i <= n; i++) res = max(res, f[n][i]);

    cout << res << endl;

    return 0;
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114242851