洛谷 p1439 最长公共子序列

题目描述

给出1-n的两个排列P1和P2,求它们的最长公共子序列。

输入输出格式

输入格式:

第一行是一个数n,

接下来两行,每行为n个数,为自然数1-n的一个排列。

输出格式:

一个数,即最长公共子序列的长度

输入输出样例

输入样例#1: 
5 
3 2 1 4 5
1 2 3 4 5
输出样例#1: 
3

n^2做法
我们定义一个数组dp[i][j],表示第一个序列的前i项,第二个序列前j项中最长公共子序列
那么可以轻易地得出dp方程:
如果当前a[i]=b[j],dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
否则 dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
但是这个做法很明显只能过50%的点,其他的都会Tle;

nlogn做法
仔细一点,观察题目,可以知道,两个序列中数字是一样的,只不过是顺序变化了
所以我们可以先将a序列中每个位置存起来 打个比方
n=8 a: 8 7 6 5 4 3 2 1
b: 8 6 4 3 1 2 5 7
那么,1到8这些数字在a序列中位置分别为:8,7,6,5,4,3,2,1
那么b序列中每个数在a中的位置是:1,3,5,6,8,7,4,2 设这个数组为wz[]
很显然, 如果在wz数组中间呈上升趋势即为一段公共子序列
那么就可以转换成为求最长不下降子序列
所以就把这道题转换成为一个较为初级的题目
AC代码
#include<bits/stdc++.h>
using namespace std;

const int N=100002;
int number,a[N],b[N],wz[N],f[N],len=0;

int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

int main(){
    number=read();
    for(int i=1;i<=number;i++)a[i]=read(),wz[a[i]]=i;
    for(int i=1;i<=number;i++)b[i]=read(),b[i]=wz[b[i]],f[i]=1e9;
    f[0]=0;        
    for(int i=1;i<=number;i++){
        int l=0,r=len;
        if(b[i]>f[len])f[++len]=b[i];
        else{
            while(l<r){
                int mid=(l+r)>>1;
                if(f[mid]>b[i])r=mid;
                else l=mid+1;
            }
            f[l]=min(f[l],b[i]);
        }
    }
    cout<<len;
    return 0;
}


 
原文地址:https://www.cnblogs.com/GMSD/p/11229077.html