p275

d(i)是以Ai为结尾的最长上升子序列的长度

《算法竞赛入门经典-训练指南》p62 问题6 提供了一种优化到 O(nlogn)的方法。

文本中用g(i)表示d值为i的最小状态编号(数组下标),满足

g(1) <= g(2) <= g(3) <= ... <= g(n)

可以用反证法:

假设 i < j, g(i) > g(j)

g(j)代表 d(x) = j 的最小 x,对应的LIS的长度为j,最后一个元素是Ax,设这个LIS为LISj

g(i)代表 d(y) = i 的最小 y,对应的LIS的长度为i,最后一个元素是Ay

LISj有j个成员,其中取i个成员(不包括最后一个元素)也是一个LIS,长度为i,这个LIS的最后一个成员的下标 < g(j) < g(i),而 g(i) 应该是长度为i的LIS的最小下标,所以矛盾,得证 #

代码中实际上是用g(i)表示d值为i的多个LIS中,最后一个元素最小的LIS的最后一个元素的值,也满足

g(1) <= g(2) <= g(3) <= ... <= g(n)

可以用反证法:

假设 i < j, g(i) > g(j)

g(j)代表 d(x) = j 的最小 x,对应的LIS的长度为j,最后一个元素是x,设这个LIS为LISj

g(i)代表 d(y) = i 的最小 y,对应的LIS的长度为i,最后一个元素是y

LISj有j个成员,其中取i个成员(不包括最后一个元素)也是一个LIS,长度为i,这个LIS的最后一个成员的值 = z < g(j) = x < g(i) = y,而 g(i) 应该是长度为i的LIS的末尾元素最小值,所以矛盾,得证 #

    for (int i = ; i <= n; i++) g[i] = INF;

    for (int i = ; i < n; i++) {
// k是满足 g[k] >= A[i] 的最小下标
// k' = k - 1 是满足 g[k] < A[i] 的最大(最后一个)下标,A[i] 可以放在g[k-1]对应的长度为k-1的LIS的后面,形成一个新的长度为k的LIS
// 这个新的LIS的长度为k,最后一个元素是A[i], 所以设置 d[i] = k;
// 此时g[k] >= A[i], 所以设置 g[k] = A[i];
int k = lower_bound(g + , g + n + , A[i]) - g; // 在g[1]到g[n]中找
d[i] = k;
g[k] = A[i];
}

数组g[]是排序的

最后的答案是 lower_bound(g + 1, g + 1+ n, INF),也就是数组g[]中不是INF的最大下标

《挑战程序设计竞赛》p64 也有类似分析

 网上有个例子

假设存在一个序列A[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n 下面一步一步试着找出它。 我们定义一个序列g,然后令 i = 1 to 9 逐个考察这个序列。 此外,我们用一个变量Len来记录现在最长算到多少了

首先,把A[1]有序地放到g里,令g[1] = 2,就是说当只有1个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把A[2]有序地放到g里,令g[1] = 1,就是说长度为1的LIS的最小末尾是1,A[1]=2已经没用了,很容易理解吧。这时Len=1

接着,A[3] = 5,A[3]>g[1],所以令g[1+1]=g[2]=A[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候g[1..2] = 1, 5,Len=2

再来,A[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候g[1..2] = 1, 3,Len = 2

继续,A[5] = 6,它在3后面,因为g[2] = 3, 而6在3后面,于是很容易可以推知g[3] = 6, 这时g[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, A[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到g[3] = 4。g[1..3] = 1, 3, 4, Len继续等于3

第7个, A[7] = 8,它很大,比4大,嗯。于是g[4] = 8。Len变成4了

第8个, A[8] = 9,得到g[5] = 9,嗯。Len继续增大,到5了。

最后一个, A[9] = 7,它在g[3] = 4和g[4] = 8之间,所以我们知道,最新的g[4] =7,g[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。

虽然最后一个A[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到A[5], 9更新到A[6],得出LIS的长度为6。

然后应该发现一件事情了:在g中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

另一个网上的说明

对于序列Sn,考虑其长度为i的单调子列(1<=i<=m),这样的子列可能有多个。我们选取这些子列的结尾元素(子列的最后一个元素)的最小值。用Li表示。易知

L1<=L2<=…<=Lm

如果Li>Lj(i<j),那么去掉以Lj结尾的递增子序列的最后j-i个元素,得到一个长度为i的子序列,该序列的结尾元素ak<=Lj<Li,这与Li标识了长度为i的递增子序列的最小结尾元素相矛盾,于是证明了上述结论。

现在,我们来寻找Sn对应的L序列,如果我们找到的最大的Li是Lm,那么m就是最大单调子列的长度。下面的方法可以用来维护L。

从左至右扫描Sn,对于每一个ai,它可能

(1)    ai<L1,那么L1=ai

(2)    ai>=Lm,那么Lm+1=ai,m=m+1 (其中m是当前见到的最大的L下标)

(3)    Ls<=ai<Ls+1,那么Ls+1=ai

 

扫描完成后,我们也就得到了最长递增子序列的长度。从上述方法可知,对于每一个元素,我们需要对L进行查找操作,由于L有序,所以这个操作为logn,于是总的复杂度为O(nlogn)。

lrj 9.4.1 最长上升子序列 LIS的更多相关文章

  1. 2.16 最长递增子序列 LIS

    [本文链接] http://www.cnblogs.com/hellogiser/p/dp-of-LIS.html [分析] 思路一:设序列为A,对序列进行排序后得到B,那么A的最长递增子序列LIS就 ...

  2. 最长上升子序列LIS(51nod1134)

    1134 最长递增子序列 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递 ...

  3. 动态规划(DP),最长递增子序列(LIS)

    题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(d ...

  4. 【部分转载】:【lower_bound、upperbound讲解、二分查找、最长上升子序列(LIS)、最长下降子序列模版】

    二分 lower_bound lower_bound()在一个区间内进行二分查找,返回第一个大于等于目标值的位置(地址) upper_bound upper_bound()与lower_bound() ...

  5. 题解 最长上升子序列 LIS

    最长上升子序列 LIS Description 给出一个 1 ∼ n (n ≤ 10^5) 的排列 P 求其最长上升子序列长度 Input 第一行一个正整数n,表示序列中整数个数: 第二行是空格隔开的 ...

  6. 最长回文子序列LCS,最长递增子序列LIS及相互联系

    最长公共子序列LCS Lintcode 77. 最长公共子序列 LCS问题是求两个字符串的最长公共子序列 \[ dp[i][j] = \left\{\begin{matrix} & max(d ...

  7. 一个数组求其最长递增子序列(LIS)

    一个数组求其最长递增子序列(LIS) 例如数组{3, 1, 4, 2, 3, 9, 4, 6}的LIS是{1, 2, 3, 4, 6},长度为5,假设数组长度为N,求数组的LIS的长度, 需要一个额外 ...

  8. 1. 线性DP 300. 最长上升子序列 (LIS)

    最经典单串: 300. 最长上升子序列 (LIS) https://leetcode-cn.com/problems/longest-increasing-subsequence/submission ...

  9. 动态规划 - 最长递增子序列(LIS)

    最长递增子序列是动态规划中经典的问题,详细如下: 在一个已知的序列{a1,a2,...,an}中,取出若干数组组成新的序列{ai1,ai2,...,aim},其中下标i1,i2,...,im保持递增, ...

随机推荐

  1. ScrollView 实现子视图滑动到顶部时固定不动

    这个,个人建议使用自己写的布局使用view的gon或者visble的方法,使用design包中的控件来的话,局限性很大 方法有倆 (1)自定义ScrollView 重写ScrollView 的 com ...

  2. Kryo官方文档-中文翻译

    Kryo作为一个优秀的Java序列化方案,在网上能找到不少测评,但未见系统的中文入门或说明文档.官方文档是最好的学习文档.虽然英文不差,但啃下来毕竟没母语来的舒服.这里抽出时间做些翻译,以方便大家查阅 ...

  3. [运维]ESXI Web Client 标签: vmware运维服务器 2017-05-28 20:59 597人阅读 评论(9)

    ESXI安装完成之后,配置好ip,我们就可以使用了?那么如何使用呢?一般来说有两种方式,一种是安装专门的管理客户端,client端,另一种更加方便,就是使用web client端. 下面来介绍一下es ...

  4. AC自动机fail树小结

    建议大家学过AC自动机之后再来看这篇小结 fail树就是讲fail指针看做一条边连成的树形结构 fail指针在AC自动机中的含义是指以x为结尾的后缀在其他模式串中所能匹配的最长前缀的长度 所以在模式串 ...

  5. 【水滴石穿】rn_antd_dva_reactnavigation

    这个项目好像就是记录了一个数据的流向,大体思想好像是这个 项目地址:https://github.com/Yangzhuren/rn_antd_dva_reactnavigation 先看效果 第一个 ...

  6. WCF ChannelFactory

    public static class WcfExtensions{    public static void Using<T>(this T client, Action<T&g ...

  7. Mac查看Python安装路径和版本

    目录 #查看当前所有Python版本路径 appledeMBP:~ apple$ which python2.7 /usr/local/bin/python2.7 appledeMBP:~ apple ...

  8. oracle水线的定义

    1.水线定义了表的数据在一个BLOCK中所达到的最高的位置. 2.当有新的记录插入,水线增高 3.当删除记录时,水线不回落 4.减少查询量

  9. 国外最受欢迎的十大社交APP网站

    国外最受欢迎的十大社交APP网站 2016-11-01 09:34悠悠国外网     有哪些好的国外社交软件你知道吗,想使用国外流行的社交应用来体验不一样的社交么,想和外国友人交朋友么.本期悠悠国外网 ...

  10. Android开发-API指南-<activity-alias>[原创译文]

    http://blog.sina.com.cn/s/blog_48d491300100zmg5.html