求最长上升子序列LIS(两种解法)+ LCS向LIS问题的转化

概念 

Loggest Increasing Subsequence最长上升子序列

Loggest Common Subsequnce: 最长公共子序列(两个或多个序列的问题)

概念就不解释了,,,大家应该都懂

解法

O(nlog(n))的解法

这种解法是贪心+二分的思想。实际并不难,比下面的O(n^2)复杂度的解法还更容易理解。

这种算法的思想大致如下:

现在有 4 2 3 8 9 7 这个序列,下面以他为例,说明求最长子序列的长度。

首先定义一个数组,保存这个最长的上升子序列的每一个元素,设为dp数组。

下面我们就要依次往里面放元素了:

        第一个元素是4,直接放进dp。                                                      || 当前dp中元素的情况:4

        第二个元素是2,比4小。把dp中的2替换成4.(下面介绍为什么)||当前dp中元素的情况:2

       第三个元素是3,比2大,放到dp数组中2的后面                             ||当前dp中元素的情况:2 3

       第四个元素是8,比3大,放到dp数组中3的后面                             ||当前dp中元素的情况:2 3 8

       第五个元素是9,比8大,放到dp数组中8的后面                             ||当前dp中元素的情况:2 3 8 9

       第六个元素是7,替换dp数组中的8                                                 ||当前dp中元素的情况:2 3 7 9

       最后结果:最长公共子序列的长度是4.

结论:每次进来一个数 ,他总是替换dp数组从左往右第一个大于它的数

而存到dp数组中的元素,不一定就是真实的元素。上面的例子的最长上升子序列应该是 2 3 8 9.所以这个数组只是个伪序列,真正有用的是数组的长度。

我现在只能做到这里,证明我也不会。。。。。。。。。但就是这样子。

O(n^2)做法

这种做法,各种数据结构的书上应该都有吧。只要找到状态转移方程:

dp[i]=max(dp[j])+1; //条件是1=<j<i 并且 a[i]>a[j] .

很容易就可以写出代码

 



LCS转LIS

这便是这篇博客的意义所在了,LIS算法有O(nlogn)的算法,而LCS没有。

看这样一道题:P1439

题目描述

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

输入格式

第一行是一个数 n。

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

输出格式

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

输入输出样例

输入 #1复制

5 
3 2 1 4 5
1 2 3 4 5

输出 #1复制

3

说明/提示

  • 对于50% 的数据, n≤10^3;
  • 对于100% 的数据, n≤10^5。

分析:

这道题n达到了10^5,用经典的LCS解法,显然超时。这就需要将LCS向LIS转化

用到的思想:离散化。

举例:如下两个序列

4 2 3 8 9

8 4 2 3 9

做一步离散化处理,不考虑每个元素具体的值,用新数组每个元素表示原数组中元素所在的位置

1 2 3 4 5

4 1 2 3 5

说明:上述第一行表示原序列的4 2 3 8 9的每一个位置,也是就8在原序列第一个序列中的第一位、4在第1位、2在第二位、3在第三位、9在第5位。

---

理解上述的操作之后,下面观察生成的第二个序列的规律:只要求生成的第二个序列的最长上升序列,就可以得到LCS

ok,代码如下,仅供参考

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAX = 5e5+5;

int N;
int a[MAX],b[MAX];
int dp[MAX];
int belong[MAX*100]; //离散化的关键

int main() {
    cin >> N;
    for (int i = 1; i <= N; ++i) {
        cin >> a[i];
        belong[a[i]]=i;
    }
    for (int i = 1; i <= N; ++i) {
        cin >> b[i];
        b[i]=belong[b[i]];
    }
    int len = 0;
    memset(dp,0,sizeof(dp));
    for (int i = 1; i <= N; ++i) {
        if(dp[len]<b[i]) {
            dp[++len]=b[i];
        } else {
            *lower_bound(dp+1,dp+len+1,b[i])=b[i]; //二分查找,降低时间复杂度
        }
    }
    cout << len;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值