最长公共上升子序列

题目描述

熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列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;
}
  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值