最长公共上升子序列 题解

原题链接:LCIS

题目大意

给定了两个长度为\(n,m\)的序列,找出他们的最长公共子序列,要求严格上升,只需要长度.例如两个序列分别是1 1 2 31 2 3那么答案是1 2 3长度为3.

数据范围:\(1 \leq n,m \leq 3000\)

思路

显然从复杂度上来说这个题要求的是一个\(O(n^2)\)的解法.那么由于这个题本身是来自两个经典模型LCS和LIS拼接过来的,那么比较容易的就可以想到这个题所需的几个关键信息:两个序列分别走到哪个位置以及当前末尾的是谁.

状态定义:\(f[i][j]\)表示当前A序列用到了\([1,i]\)而B序列用到了\([1,j]\)且以\(B[j]\)作为末尾元素的LCIS的所有方案中长度最大值.

入口:全是\(0\),方便起见把\(A[0],B[0]\)记作\(-inf\).

转移:可以类比原来的两个模型得到:

  • \(A[i] \neq B[j]\)的时候,只能退而求其次,因为这两个位置不匹配我就只能拿上一个以\(B[j]\)结尾的状态转移,这样就是最大的不至于更差,也就是说这种情况下\(f[i][j] = f[i - 1][j]\)
  • \(A[i] = B[j]\)的时候,这里两个末尾的值匹配起来了,那么上一个,也就是之前的一个可以拼接过来的状态,首先应该是用到了A里的前\(i-1\)个字符,同时是以某个\(k\in[1,j-1],B[k]\)作为结尾元素的,那么同时需要是一个严格递增的序列,所以还要保证\(B[k]<B[j]\).那么可以写出\(f[i][j] = \max_{k\in[1,j-1],B[k] < B[j]}f[i-1][k] + 1\)同时由于\(B[k]=A[i]\)那么也可以替换得到\(f[i][j] = \max_{k\in[1,j-1],B[k] < A[i]}f[i-1][k] + 1\)

出口:\(\max_{i\in[1,m]}f[n][i]\)

那么上述的状态计算可以以\(O(n^3)\)的暴力递推求解.但这显然不够,还得要把这个复杂度降掉一维,或者至少也要降一个\(log\).那么考虑如何降掉一维,一个一个来看的话,首先枚举\(i,j\)肯定是避无可避的,想要降只能动这个\(k\).联系到之前的一个转换,即\(B[k]=A[i]\)这一步,不妨把\(j\)进行递推转移的时候,看做\(i\)是一个常数,也就是\(A[i]\)是一个常数,现在就不看\(i\)是个什么情况只看\(j\)了.那么状态转移方程在这一维就是\(f[j] = \max_{k\in[1,j-1],B[k] < C}f[k]+1\),可以注意到很关键的一点,那就是既然这个条件是一个小于常数且只取所有满足条件的值里的最大值,那么不妨就把之前可以拿来转移的最大值记录下来,进而可以观察到这个式子的一个特点就是当\(j\)增大的时候,\(k\)的决策集合只会增加一个数,就是说当\(j\)增大的时候,所有\(k\)的取值范围里才可能增加一个,这是一个"决策集合里的元素只增加不减少"的情况,而且只会选取里面的最大值.因此可以从最开始记录一个最大值,每个\(j\)增加的时候尝试把当前一个新的\(k\)放进决策集合,并尝试更新最大值,如果需要最大值,那么就直接拿过来用就可以了.由此就可以把整个题的复杂度降到\(O(n^2)\).

在最开始的时候,最大值应该设置成\(1\).因为开始的时候对应过去的元素应该是\(B[0]\),而他显然是满足\(B[0]<A[i]\)的.

代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3005;
int a[N],b[N],f[N][N];
int main()
{
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;++i)   scanf("%d",&a[i]);
    for(int i = 1;i <= n;++i)   scanf("%d",&b[i]);
    
    for(int i = 1;i <= n;++i)
    {
        int maxv = 1;
        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);
            if(b[j] < a[i])     maxv = max(maxv,f[i][j] + 1);
        }
    }
    int res = 0;
    for(int i = 1;i <= n;++i)   res = max(res,f[n][i]);
    cout << res;
    return 0;
}
posted @ 2020-09-01 13:34  随处可见的阿宅  阅读(239)  评论(0编辑  收藏  举报