P1439【模板】最长公共子序列 题解

· · 个人记录

观察数据范围

说明/提示

50pt

显然,50分的数据可以用O(n^2)的时间复杂度

采用DP,令a数组为p_1b数组为p_2

$$ c[i][j]= \begin{cases} max(c[i-1][j],c[i][j-1]) &\ a[i] \not =b[j]\\ max(c[i-1][j],c[i][j-1],c[i-1][j-1]+1) &\ a[i]=b[j] \end{cases} $$ 这个很好理解,就不过多解释了 ```cpp #include<bits/stdc++.h> typedef unsigned long long ull; typedef long long ll; using namespace std; const int N=1005; int a[N],b[N],c[N][N]; int main() { int i,j,n; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); } for(i=1;i<=n;i++) { scanf("%d",&b[i]); } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { c[i][j]=max(c[i-1][j],c[i][j-1]); if(a[i]==b[j]) { c[i][j]=max(c[i][j],c[i-1][j-1]+1); } } } printf("%d",c[n][n]); return 0; } ``` ## $100pt

看数据, n \le 10^5 ,肯定是O(N log N)或者是O(N)的时间复杂度,O(N)不可能,所以选择前者

我们采用离散化,初始化:d[a[i]]=i

因为两个序列都是1\sim n,所以如果把b[i]映射到d[a[i]],之后这个问题就变成了最长不下降子序列的问题

我们用样例来进行解释(用到的数组名称都是我代码里面的):

5 
3 2 1 4 5
1 2 3 4 5

对a数组进行离散化后得到(下面一行是分别对应的真正数)

d=&\{1,2,3,4,5\} \\&3,2,1,4,5

b数组离散化后:{3,2,1,4,5}

仔细思考一下,为什么是最长不下降子序列而不是下降?

因为离散化本质上就是把数值对应成了位置,只有当b数组不下降时才能使得有公共子序列

其实求最长上升子序列也可以,都一样

接下来就是求最长不下降子序列的问题了:

普通算法是O(n^2)

显然和前面50pt没啥区别,所以我们要用二分查找和贪心的思想来优化,时间复杂度为O(nlogn)

具体策略如下(d数组还是离散化的数组):

数组c表示目前的最长不下降子序列,长度为m

通过离散化和优化最长不下降子序列,这道题就完成了,建议配合代码食用

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N=100005;
int a[N],b[N],c[N],d[N];
int main()
{
    int m=0,i,k,n;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        d[a[i]]=i;//离散化
    }
    for(i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
    }
    for(i=1;i<=n;i++)
    {
        if(d[b[i]]>=c[m])//第一种情况
        {
            m++;
            c[m]=d[b[i]];
            continue;
        }
        k=lower_bound(c+1,c+1+m,d[b[i]])-c;//这个函数是的意思是找出c数组中第一个>=d[b[i]]的位置(因为用到二分,所以数组要求有序)
        c[k]=d[b[i]]; //替换
    }
    printf("%d",m);
    return 0;
}