CF10D LCIS 最长公共上升子序列

假装没事ソ 提交于 2020-02-09 00:42:55

题目描述

This problem differs from one which was on the online contest.

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

The sequence s1,s2,...,sk s_{1},s_{2},...,s_{k} s1,s2,...,sk is called the subsequence of the sequence a1,a2,...,an a_{1},a_{2},...,a_{n} a1,a2,...,an , if there exist such a set of indexes 1<=i1<i2<...<ik<=n 1<=i_{1}<i_{2}<...<i_{k}<=n 1<=i1<i2<...<ik<=n that aij=sj a_{ij}=s_{j} aij=sj . In other words, the sequence s s s can be derived from the sequence a a 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.

输入格式

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

输出格式

In the first line output k k 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.

题意翻译


求两个串的最长公共上升子序列。
说来惭愧,这个题看了蓝书以及洛谷题解半天才勉强想通。LCS与LIS的转移方程不难想,但这个题是LIS与LCS的组合。一般来说类似的题这种多维dp数组的含义是从前i个数/...选择一些数,但这里如果按这种状态定义的话,考虑到LIS问题的求解,我们就无法获得最后位置的信息。所以按照蓝书,这里定义dp[i][j]是A1~AI与B1~B就可以构成的以B[j]为结尾的LCIS的长度。
然后考虑转移方程。
当A[i]!=B[j]时,dp[i][j]=dp[i-1][j]。这里很好理解,A[i]与B[j]都不相等了,自然可以往前推一个。要注意的是这里dp数组第二维的含义,还是以B[j]结尾,所以i->i-1而j不变。
当A[i]==B[j]时,说明AB两串至少有A[i]或者说B[j]这个公共元素了,再次考虑dp数组的定义,以B[j]为结尾的LCIS的长度”,自然想到找到一个末尾小于B[j]的串把B[j]给接上去。因此转移方程可以写成dp[i][j]=max(dp[i-1][k]+1|k:1 to j-1)。当然从决策集合考虑还可以优化成O(n^2),但看这个题的数据范围目前这么写足以过掉。
下面来看存储答案。这里借鉴了洛谷题解的巧妙思想,再创建一个二维数组out,其含义为:out[p][q]代表以B[p]结尾的LCIS的前q个元素。这样一来,在第二重for循环中,当遇到A[i]!=B[j]时,自然不用更新;当A[i]==B[j]时,首先令out[j][1]=b[j],这代表目前LCIS起码有一个元素B[j]。然后第三重循环遍历时遇到可以接上B[j]的,如果比当前dp[i][j]大,在更新dp[i][j]的
同时也更新out[j][1~dp[i-1][k]]。
 
#include <bits/stdc++.h>
using namespace std;
int n1,n2;
int a[505];
int b[505];
int dp[505][505]={0};
int out[505][505];
int main()
{
    int i,j,k,m;
    int ans=0;
    cin>>n1;
    memset(dp,0,sizeof(dp));
    for(i=1;i<=n1;i++)
    {
        scanf("%d",&a[i]);
    }
    cin>>n2;
    for(j=1;j<=n2;j++)
    {
        scanf("%d",&b[j]);
    }
    dp[0][0]=0;
    int pos=0;
    for(i=1;i<=n1;i++)
    {
        for(j=1;j<=n2;j++)
        {
            
            if(a[i]!=b[j])
            {
                dp[i][j]=dp[i-1][j];//和之前一样的话没必要更新输出串 
            }
            else
            {
                //
                dp[i][j]=1;//相等的话最小也为1 
                out[j][1]=b[j];//相等的话最少有一个b[j]    
                //
                for(k=1;k<j;k++)//找到最长的进行拼接 
                {
                    if(b[k]<b[j])
                    {
                        if(dp[i-1][k]+1>dp[i][j])//大于当前答案的话 
                        {
                            dp[i][j]=dp[i-1][k]+1;//更新dp数组 
                            for(m=1;m<=dp[i-1][k];m++)//更新out数组对应位置的元素 
                            {
                                out[j][m]=out[k][m];
                            }
                            out[j][dp[i][j]]=b[j];//把b[j]加上去 
                        }
                    }                
                }    
            }    
            ans=max(ans,dp[i][j]);//更新LCIS的长度 
            if(ans==dp[i][j])pos=j;    //找到对应的位置        
        }    
    }
    cout<<ans<<endl;
    for(i=1;i<=ans;i++)//输出LCIS 
    {
        cout<<out[pos][i]<<' ';
    }
    
    return 0;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!