lrj 9.4.1 最长上升子序列 LIS
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的更多相关文章
- 2.16 最长递增子序列 LIS
[本文链接] http://www.cnblogs.com/hellogiser/p/dp-of-LIS.html [分析] 思路一:设序列为A,对序列进行排序后得到B,那么A的最长递增子序列LIS就 ...
- 最长上升子序列LIS(51nod1134)
1134 最长递增子序列 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递 ...
- 动态规划(DP),最长递增子序列(LIS)
题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(d ...
- 【部分转载】:【lower_bound、upperbound讲解、二分查找、最长上升子序列(LIS)、最长下降子序列模版】
二分 lower_bound lower_bound()在一个区间内进行二分查找,返回第一个大于等于目标值的位置(地址) upper_bound upper_bound()与lower_bound() ...
- 题解 最长上升子序列 LIS
最长上升子序列 LIS Description 给出一个 1 ∼ n (n ≤ 10^5) 的排列 P 求其最长上升子序列长度 Input 第一行一个正整数n,表示序列中整数个数: 第二行是空格隔开的 ...
- 最长回文子序列LCS,最长递增子序列LIS及相互联系
最长公共子序列LCS Lintcode 77. 最长公共子序列 LCS问题是求两个字符串的最长公共子序列 \[ dp[i][j] = \left\{\begin{matrix} & max(d ...
- 一个数组求其最长递增子序列(LIS)
一个数组求其最长递增子序列(LIS) 例如数组{3, 1, 4, 2, 3, 9, 4, 6}的LIS是{1, 2, 3, 4, 6},长度为5,假设数组长度为N,求数组的LIS的长度, 需要一个额外 ...
- 1. 线性DP 300. 最长上升子序列 (LIS)
最经典单串: 300. 最长上升子序列 (LIS) https://leetcode-cn.com/problems/longest-increasing-subsequence/submission ...
- 动态规划 - 最长递增子序列(LIS)
最长递增子序列是动态规划中经典的问题,详细如下: 在一个已知的序列{a1,a2,...,an}中,取出若干数组组成新的序列{ai1,ai2,...,aim},其中下标i1,i2,...,im保持递增, ...
随机推荐
- 微信小程序--轮播图,标题,盒子,tab栏的合成例子
小程序是什么? 微信小程序,是一种不需要下载安装即可使用的应用,用户扫一扫或搜一下即可打开应用,在微信-发现-小程序可打开应用. 一.小程序的样式编写: 目录结构: app.json { " ...
- python中数字转换成字符串
数字转换成字符串: num=123 str='%d' %num str就变成了"123"
- Android消息机制使用注意事项,防止泄漏
在Android的线程通信当中,使用频率最多的就是Android的消息处理机制(Handler.send().View.post().Asynctask.excute()等等都使用到了消息处理机制). ...
- Redhad的开源Paas平台:OpenShift
参考redHat的官方文章翻译而来:https://openshift.redhat.com/community/wiki/architecture-overview OpenShift Origin ...
- JavaScript实现,控制一个文本框只能输入正整数,如输入不符合条件则文本框全部字体标红
腾讯2014年研发职位笔试题Web前端方向简单题第一题. 代码: <html> <head> <title>test JavaScript</title> ...
- HTML5入门指南
1.HTML5到底是什么? HTML5是HTML最新的修订版本,2014年10月由万维网联盟(W3C)完成标准制定.目标是取代1999年所制定的HTML 4.01和XHTML 1.0标准,以期能在互联 ...
- 从零学React Native之10Text
在React Native开发中,所有需要显示的字符串文本都需要放置在Text或者Text的子组件中.虽然在之前的文章中多次使用了Text组件,但是Text组件还是值得专门学习的, 并没有想象中的那么 ...
- spoj SUBLEX (Lexicographical Substring Search) RE的欢迎来看看
SPOJ.com - Problem SUBLEX 这么裸的一个SAM,放在了死破OJ上面就是个坑. 注意用SAM做的时候输出要用一个数组存下来,然后再puts,不然一个一个字符输出会更慢. 还有一个 ...
- 中断源记录 INT0 INT1
中断源记录 INT0 INT1 用到一个单片机 使用的 P3.1 P3.3 作为唤醒口,后来发一 P3.1 和 P3.3 使用的同一个中断 INT1,这个尴尬了,只能两选 一. 查看规格书,还好 P3 ...
- 2019-10-22-Roslyn-打包自定义的文件到-NuGet-包
title author date CreateTime categories Roslyn 打包自定义的文件到 NuGet 包 lindexi 2019-10-22 19:45:34 +0800 2 ...