洛谷P1439 排列LCS问题

题目描述

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

输入输出格式

输入格式:

第一行是一个数n,

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

输出格式:

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

输入输出样例

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

说明

【数据规模】

对于50%的数据,n≤1000

对于100%的数据,n≤100000

分析:经典的LCS做法只有50分,100分的做法很显然是O(nlogn)的,将n改为logn能想到的就是二分,但是在LCS问题中要求的是最长公共的,并不满足二分的性质,只有是单调的才能用二分,考虑转化为LIS来做,如果两个序列都是单调上升的,那么答案就是第二个序列的最长上升子序列,关键是题目给的并不是单调上升的,怎么解决呢?我们把第一个序列编号,然后第二个序列按照第一个序列的编号来改动一下,这样第一个序列就是单调上升的了,我们只需要找到第二个序列的最长上升子序列就好了.

      知道要二分,但是要二分什么东西呢?有一种神奇的做法,维护一个栈,考虑第i个元素j,如果j大于栈顶元素,则直接放到栈顶,否则二分找到第一个大于j的数,用j把他替换掉,答案就是栈的大小.原理是什么呢?因为我们是从左往右扫的,根据贪心思想,最后一个数越小越好,替换操作并不是真的替换,而是表示以后的数可以插入在这个位置,毕竟我们只要栈大小,不要求具体的数.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

int n,s1[100010],s2[100010],st[100010],top,mm[100010];

int erfen(int x)
{
    int l = 1,r = top;
    while (l < r)
    {
         int mid = (l + r) >> 1;
         if (st[mid] <= x)
         l = mid + 1;
         else
         r = mid;
    }
    return r;
}

int main()
{
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
    scanf("%d",&s1[i]);
    for (int i = 1; i <= n; i++)
    scanf("%d",&s2[i]);
    for (int i = 1; i <= n; i++)
    mm[s1[i]] = i;
    for (int i = 1; i <= n; i++)
    s2[i] = mm[s2[i]];

    for (int i = 1; i <= n; i++)
    {
        if (s2[i] > st[top])
        st[++top] = s2[i];
        else
        st[erfen(s2[i])] = s2[i];
    }
    printf("%d
",top);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7478186.html