题目地址:
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 1≤n≤3000,序列中的数字均不超过 2 31 − 1 2^{31}−1 231−1
思路是动态规划。设 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 i−1的前缀和 B B B的长 j j j的前缀里的最长公共上升子序列,那么长度就是 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][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] max1≤j≤nf[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[i−1][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<j∧B[k]<B[j]=A[i]f[i−1][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[i−1][j],1+k<j∧B[k]<A[i]maxf[i−1][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<j∧B[k]<A[i]f[i−1][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)。