题目描述
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。不过,只要告诉奶牛它的长度就可以了。数列A和B的长度均不超过3000。
输入格式
第一行N,表示A,B的长度。
第二行,串A。
第三行,串B。
输出格式
一行:一个整数,表示最长公共上升子序列的长度。
输入输出样例
输入样例1:
4
2 2 1 3
2 1 2 3
输出样例1:
2
【耗时限制】1000ms 【内存限制】128MB
最长上升子序列和最长公共子序列的合体,结合LIS和LCS,按照序列长度划分阶段。
定义状态: d(i,j)表示序列A[1,i]和序列B[1,j]
中以B[j]结尾的最长公共子序列,ans = max(d(i,j))。
状态转移方程:
①A[i] != B[j],此时由于必须以B[j]结尾,所以:d[i][j] = d[i-1][j]。
②A[i] == B[j],d[i][j] = max(d[i-1][k]) + 1; (1 <= k < j && B[k] < B[j])
时间复杂度:O(N^3),N最大3000,会超时!
那就需要进一步优化
如果a[i] == b[j],可以将b[k] < b[j]改写为b[k] < a[i];
第2层for循环在枚举j的过程中i始终保持不变,a[i]也就保持不变。
j从x变为x+1时,k的取值范围多了一个x, max(d[i-1][k])相较于j=x的情况多了一个d[i-1][x]。
设maxv = max(d[i-1][k]); (1 <= k < x && b[k] < a[i]);
① b[x] >= a[i],则d[i][x+1]依赖的也是maxv,所有等于a[i]的b[j]可以共享maxv。
② b[x] < a[i],则d[i][x+1]依赖的是maxv和d[i-1][x]中的较大值。
在枚举j的过程中,维护maxv的值,这样可以将第3层for循环省掉,时间复杂度:O(N^2)。
上面都是些片段代码,这里上一个完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <algorithm>
using namespace std;
int a[3005],b[3005];
int d[3005][3005];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int j=1;j<=n;j++) scanf("%d",&b[j]);
int ans=0;
for(int i=1;i<=n;i++){
int maxv=0;
for(int j=1;j<=n;j++){
if(b[j-1]<a[i]) maxv=max(maxv,d[i-1][j-1]);
if(a[i]==b[j]) d[i][j]=maxv+1;
else d[i][j]=d[i-1][j];
ans=max(ans,d[i][j]);
}
}
cout<<ans;
return 0;
}