最长公共上升子序列

Posted zcxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长公共上升子序列相关的知识,希望对你有一定的参考价值。

输入格式

第一行包含一个整数N,表示数列A,B的长度。
第二行包含N个整数,表示数列A。
第三行包含N个整数,表示数列B。

输出格式

输出一个整数,表示最长公共上升子序列的长度。

数据范围

1≤N≤3000,序列中的数字均不超过231?1

思路:

状态表示:f[i][j]表示A数组中的前i个元素以及B数组的前j个元素,且以B[j]结尾的最长公共上升子序列
状态计算:分情况讨论,当A[i]不包含在LICS中时,那么f[i][j] == f[i-1][j]
当A[i]包含在LICS中时,由于B[j]是结尾元素,那么一定有A[i]==B[j],然后遍历1~j的所有元素k,当B[j] > B[k]时,f[i][j] == max(f[i][j], f[i-1][k] + 1)

Code: O(n3)

#include <bits/stdc++.h>

using namespace std;

const int N = 3010;

int n;
int a[N],b[N];
int f[N][N];

int main()
{
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> a[i];
    for(int i = 1;i <= n;i++) cin >> b[i];
    
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)
        {
            f[i][j] = f[i-1][j];
            if(a[i] == b[j])
            {
                f[i][j] = max(f[i][j], 1);
                for(int k = 1;k < j;k++)
                    if(b[k] < b[j])
                        f[i][j] = max(f[i][j], f[i-1][k] + 1);
            }
        }
    }
    int ans = 0;
    for(int i = 1;i <= n;i++) ans = max(ans, f[n][i]);
    
    cout << ans << endl;
    return 0;
} 

这个时间复杂度在3000的数据范围内很容易TLE,我们可以想办法优化一下,优化方法就是把比较的地方的b[j]换成a[i]

for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)
        {
            f[i][j] = f[i-1][j];
            if(a[i] == b[j])
            {
                f[i][j] = max(f[i][j], 1);
                for(int k = 1;k < j;k++)
                    if(b[k] < a[i])
                        f[i][j] = max(f[i][j], f[i-1][k] + 1);
            }
        }
    }

当换成a[i]后我们可以发现,最内层的循环与j没有什么关系,而这层循环求的就是f[i][k]的最大值 (1 <= k < j,且b[k] < a[i] (b[j])),那么我们可以用一个变量表示最大值

for(int i = 1;i <= n;i++)
    {
        int maxv = 0;
        for(int j = 1;j <= n;j++)
        {
            f[i][j] = f[i-1][j];
            if(a[i] == b[j]) f[i][j] = max(f[i][j], maxv + 1);
            if(b[j] < a[i]) maxv = max(maxv,f[i][j]);
        }
    }

最后又发现在整个DP过程中,第一维是没有用的,可以优化掉,那么就得到了最终代码

#include <bits/stdc++.h>

using namespace std;

const int N = 3010;

int a[N],b[N];
int f[N];

int main()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> a[i];
    for(int i = 1;i <= n;i++) cin >> b[i];
    
    for(int i = 1;i <= n;i++)
    {
        int maxv = 0;
        for(int j = 1;j <= n;j++)
        {
            if(a[i] == b[j]) f[j] = max(f[j], maxv + 1);
            if(b[j] < a[i]) maxv = max(maxv,f[j]);
        }
    }
    
    int ans = 0;
    for(int i = 1;i <= n;i++) ans = max(ans, f[i]);
    cout << ans << endl;
    
    return 0;
}




以上是关于最长公共上升子序列的主要内容,如果未能解决你的问题,请参考以下文章

LIS LCS 最长上升子序列 最长公共子序列 ...

[codevs2185]最长公共上升子序列

最长公共上升子序列(codevs 2185)

最长公共上升子序列 (LIS+LCS+记录)

最长公共上升子序列

经典DP问题之最长上升子序列和最长公共子序列