题目概述:给定两个序列,求解它们的最长公共上升子序列
解题思路:
集合定义:f[i][j]:所有a[1...i]中和b[1...j]中以b[j]结尾的最长上升子序列的长度。
集合划分:不包含a[i]:等价于所有a[1...i - 1]中和b[1...j]中以b[j]结尾的最长上升子序列的长度,即f[i][j] = f[i - 1][j];
包含ai:由集合定义可知,此时若成立,则a[i] = b[j].再根据倒数第二个数是以哪个数结尾进行再次划分,即
f[i][j] = max(f[i][j],f[i - 1][k]),k = 1,2,3...j-1.
暴力版\(O(n^3)\)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3010;
int f[N][N];//f[i][j]表示a[1..i]中和b[1...j]中以b[j]结尾的最长上升公共子序列(也可以以a[i]结尾)
int a[N],b[N];
int 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++)
for(int j = 1; j <= n; j ++){
//不包含a[i]
f[i][j] = max(f[i][j],f[i - 1][j]);
//包含a[i],前提a[i] == b[j]
if(a[i] == b[j]){
int maxv = 1;
//根据倒数第二个数继续划分
for(int k = 1; k < j; k ++){
//保证是上升子序列
if(b[k] < b[j])
maxv = max(maxv,f[i][k] + 1);
}
f[i][j] = max(f[i][j],maxv);
}
}
int res = 0;
for(int i = 1; i <= n; i ++)res = max(res,f[n][i]);
cout << res << endl;
return 0;
}
由于每次在再次划分时,都会存在大量重复枚举,可以对这部分进行优化,维护一个前缀最大值。
优化版\(O(n^2)\)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3010;
int f[N][N];//f[i][j]表示a[1..i]中和b[1...j]中以b[j]结尾的最长上升公共子序列(也可以以a[i]结尾)
int a[N],b[N];
int 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++){
int maxv = 1;
for(int j = 1; j <= n; j ++){
//不包含a[i]
f[i][j] = max(f[i][j],f[i - 1][j]);
//包含a[i],前提a[i] == b[j]
if(a[i] == b[j])f[i][j] = max(maxv,f[i][j]);
if(b[j] < a[i])maxv = max(maxv,f[i - 1][j] + 1);
}
}
int res = 0;
for(int i = 1; i <= n; i ++)res = max(res,f[n][i]);
cout << res << endl;
return 0;
}
标签:...,结尾,int,公共,序列,include,最长
From: https://www.cnblogs.com/dengch/p/17742751.html