Codeforces[10D] LCIS 【最长公共上升子序列】

Description

The sequence a1, a2, ..., an is called increasing, if ai < ai + 1 for i < n.

The sequence s1, s2, ..., sk is called the subsequence of the sequence a1, a2, ..., an, if there exist such a set of indexes 1 ≤ i1 < i2 < ... < ik ≤ n that aij = sj. In other words, the sequence s can be derived from the sequence a by crossing out some elements.

You are given two sequences of integer numbers. You are to find their longest common increasing subsequence, i.e. an increasing sequence of maximum length that is the subsequence of both sequences.

Input 

The first line contains an integer n (1 ≤ n ≤ 500) — the length of the first sequence. The second line contains n space-separated integers from the range [0, 10^9] — elements of the first sequence. The third line contains an integer m (1 ≤ m ≤ 500) — the length of the second sequence. The fourth line contains m space-separated integers from the range [0, 10^9] — elements of the second sequence.

Output 

In the first line output k — the length of the longest common increasing subsequence. In the second line output the subsequence itself. Separate the elements with a space. If there are several solutions, output any.

Examples

input

7
2 3 1 6 5 4 6
4
1 3 5 6

output

3
3 5 6  

 input

5
1 2 0 2 1
3
1 0 1

 output

2
0 1 

题目大意:

给定长度小于等于 500 的数组 A,B,求他们的最长公共上升子序列。 

分析:

dp[i,j] 表示 A_1...A_i 与 B_1...B_j 可以构成的以 B_j 为结尾的LCIS的长度。

当 A_i \neq B_j 时,有 dp[i,j]=dp[i-1,j]

当 A_i = B_j 时,有 dp[i,j]=\max_{0\leq k< j,B_k<B_j}(dp[i-1,k])+1

这里因为枚举 k,O(n^3) 会超时,所以进行优化。

记录 [0,j)中所有 B_k<B_j 中 dp[i-1,k] 的最大值,直接拿来查询,那么我们把 i 作为最外层的循环,不停维护这个最大值,然后就可以把时间复杂度降到 O(n^2)

具体解释见代码。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 500 + 5;

int dp[maxn][maxn],p[maxn][maxn];
int a[maxn],b[maxn];
vector<int> v;
int n,m,ans,path;

void LCIS(int a[],int n,int b[],int m){
    for(int i=1;i<=n;i++){//外层循环
        int maxlen=0,pre=0;//maxlen代表dp[i-1][k]中的最大值,pre代表当前maxlen下的前一个B序列中选出的值(记录路径)
        for(int j=1;j<=m;j++){
            //这里假设Ai和Bj不等
            dp[i][j]=dp[i-1][j];
            p[i][j]=p[i-1][j];
            if(a[i]==b[j]&&dp[i][j]<maxlen+1){//更新dp[i][j]和p[i][j]
                dp[i][j]=maxlen+1;
                p[i][j]=pre;
            }
            if(b[j]<a[i]&&dp[i-1][j]>maxlen){//更新maxlen和pre
                maxlen=dp[i-1][j];
                pre=j;
            }
        }
    }
}

void Print(int pa) {//打印路径
    if(!pa) return;
    Print(p[n][pa]);
    cout<<b[pa]<<" ";
}

int main() {
    memset(dp,0,sizeof(dp));
    memset(p,0,sizeof(p));
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&b[i]);
    }
    LCIS(a,n,b,m);
    //选出最长路径
    for(int i=1;i<=m;i++){
        if(ans<dp[n][i]){
            ans=dp[n][i];
            path=i;
        }
    }
    printf("%d\n",ans);
    Print(path);
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值