LIS小结(O(∩_∩)O~哄哄)
~\(≧▽≦)/~啦啦啦,昨天说的是LCS,今天我们要学习的是LIS,什么是LIS呢?
LIS: 最长有序子序列(递增/递减/非递增/非递减)这么说还是有些模糊,举个例子:
在一个无序的序列a1,a2,.....,am里,找到一个最长的序列,满足ai<=aj...<=ak; 且i<j<k;
例如该无序子序列为a[1]=1,a[2]=4,a[3]=5,a[4]=2;则最长序列为1,4,5.
小盆友们是不是明白了什么是最长有序子序列了呢?下面我们说说怎么求最长有序子序列:
先来说说经典的求法吧:
设a[i]表示序列中的第i个数,f[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设f[i] = 0(i = 1, 2, ..., len(A))。则有动态规划方程:f[i] = max{1, f[j] + 1} (j = 1, 2, ..., i - 1, 且a[j] < a[i])。
现在,我们仔细考虑计算f[i]时的情况。假设有两个元素a[x]和a[y],满足
(1)y < x < i
(2)a[x] <a[y] < a[i]
(3)f[x] = f[y]
此时,选择f[x]和选择f[y]都可以得到同样的f[i]值,那么,在最长上升子序列的这个位置中,应该选择a[x]还是应该选择a[y]呢?
很明显,选择a[x]比选择a[y]要好。因为由于条件a[x] < a[y] < a[i],在a[x+1] ~a[i-1]这一段中,如果存在a[z],a[x] < a[z] < a[y],则与选择a[y]相比,将会得到更长的上升子序列。
再根据条件f[x] = f[y],我们会得到一个启示:根据f[]的值进行分类。对于f[]的每一个取值k,我们只需要保留满足f[i] = k的所有a[i]中的最小值。设d[k]记录这个值,即d[k] = min{ a[i] } ( f[i] = k )。
特别关注D[]的几个特点:
(1) D[k]的值是在整个计算过程中是单调不上升的。//此处需要特别注意!!!关键之所在!
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。
利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断a[i]与D[len],若a[i] > D[len],则将a[i]接在D[len]后将得到一个更长的上升子序列,len = len + 1,D[len+1] = a[i];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < a[i].令k = j + 1,则有D[j] < a[i] <= D[k],将a[i]接在D[j]后将得到一个更长的上升子序列,同时更新D[k] = a[i].最后,len即为所要求的最长上升子序列的长度。
在上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来的算法相比没有任何进步.但是由于D[]的特点a[x] <a[y] < a[i],我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn),有了非常显著的提高.需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列.
这个算法还可以扩展到整个最长子序列系列问题,整个算法的难点在于二分查找的设计,需要非常小心注意.
// By Fandywang 2008.7.21
// Call: LIS(a, n); 求最大递增/上升子序列(如果为最大非降子序列,只需把上面的注释部分给与替换)
const int N = ;
int a[N], f[N], d[N]; // d[i]用于记录a[0...i]的最大长度
int bsearch(const int *f, int size, const int &a)
{
int l=, r=size-;
while( l <= r )
{
int mid = (l+r)/;
if( a > f[mid-] && a <= f[mid] ) return mid; // >&&<= 换为: >= && <
else if( a < f[mid] ) r = mid-;
else l = mid+;
}
}
int LIS(const int *a, const int &n){
int i, j, size = ;
f[] = a[]; d[] = ;
for( i=; i < n; ++i ){
if( a[i] <= f[] ) j = ; // <= 换为: <
else if( a[i] > f[size-] ) j = size++; // > 换为: >=
else j = bsearch(f, size, a[i]);
f[j] = a[i]; d[i] = j+;
}
return size;
}
上面的算法多少有些繁琐,下面介绍另一种算法:
如果前i-1个数中的最长非降子序列的最后一个数是ak;那么下一步就是在求前k-1个数中的的最长非降子序列;
因此我们可以设计一个状态opt[j]表示前i个数中用到a[i]所构成的最优解。
那么决策就是在前i-1个数中找到最大的opt[j] 使得a[j]<=a[i],那么opt[j]+1 就是opt[i]的值;
方程可以这样表示:
max[opt[j]] a[i] < a[j] && 0<=j<i
opt[i] ={
max[opt[j]]+1 a[i] >= a[j] && 0<=j<i
#include <stdio.h>
#include <stdlib.h>
int main()
{
int seq[] = {,,,,,,,,,};
int opt[], i, j, max = ;
for(i=; i<; i++)
opt[i] = ;
opt[] = ; //只有一个数时最长非降序列长度为1
for(i=; i<; i++)
{
opt[i] = ;
for(j=; j<i; j++)
{
if(seq[j]<=seq[i] && opt[j]+>opt[i])
{
opt[i] = opt[j]+;
}
}
}
for(i=; i<; i++)
if(opt[i] > max)
max = opt[i];
printf("max:%d\n", max);
return ;
}
感谢:
http://www.cnblogs.com/dartagnan/archive/2011/08/29/2158230.html
http://hi.baidu.com/fandywang_jlu/item/da673a3d83e2a65980f1a7e1
LIS小结(O(∩_∩)O~哄哄)的更多相关文章
- 一些牛逼哄哄的javascript面试题
今天我们来对这5个题目详细分析一下,希望对大家有所帮助. 注: 问题来自大名鼎鼎的前端架构师Baranovskiy的帖子<So, you think you know JavaScript?&g ...
- 【转】tars源码漫谈第1篇------tc_loki.h (牛逼哄哄的loki库)
loki库是C++模板大牛Andrei写的, 里面大量运用模板的特性, 而tc_loki.h借用了loki库的部分代码, 形成了一个基本的文件tc_loki.h, 来看看: #ifndef __TC_ ...
- 牛逼哄哄的Qt库
目录 一.有价值 - 好的网站 - 好的文章 二.Qt开源库-工具 - QtXlsx--excel读写库 三.Qt开源库-控件 - libqxt编译 - Qwt - QCustomPlot - 其他 ...
- 牛逼哄哄的 Lambda 表达式,简洁优雅就是生产力!
阅读本文大概需要 4 分钟. 作者:Sevenvidia https://www.zhihu.com/question/20125256/answer/324121308 什么是Lambda? 我们知 ...
- 开发基础之牛逼哄哄的 Lambda 表达式,简洁优雅就是生产力
什么是Lambda? 我们知道,对于一个Java变量,我们可以赋给其一个“值”. 如果你想把“一块代码”赋给一个Java变量,应该怎么做呢? 比如,我想把右边那块代码,赋给一个叫做aBlockOfCo ...
- 牛逼哄哄的 API 网关是什么鬼?面试必问!
Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:aCoder2013 github.com/aCoder2013/blog/issues/35 前言 假设你正在开发一 ...
- 恕我直言,牛逼哄哄的MongoDB你可能只会30%
MongoDB闪亮登场 自我介绍 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库 ...
- 牛哄哄的celery
一.什么是Celery 1.1.celery是什么 Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度. Celery的架构由三部分组成, ...
- 牛逼哄哄的PageHelper分页插件到底是怎么实现的?网友:给我10分钟,给你写一个~
Hi,各位读者们 PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. ...
随机推荐
- NSString和NSDate的转换
输入的日期字符串形如:@"1992-05-21 13:08:08" - (NSDate *)dateFromString:(NSString *)dateString{ NSDat ...
- JVM调优总结(八)-典型配置举例2
常见配置汇总 堆设置 -Xms:初始堆大小 -Xmx:最大堆大小 -XX:NewSize=n:设置年轻代大小 -XX:NewRatio=n:设置年轻代和年老代的比值.如:为3,表示年轻代与年老代比值为 ...
- 《Python简明教程》总结
Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...
- Windows Azure 网站 (WAWS) 和中间证书
编辑人员注释:本文章由 Windows Azure 网站团队的项目经理 Erez Benari 撰写. 在 Windows Azure 网站上使用 SSL 已经司空见惯.虽然向网站上传和分配证书通常简 ...
- C++ map排序(按照value值排序)_glp_hit_新浪博客
C++ map排序(按照value值排序)_glp_hit_新浪博客 C++ map排序(按照value值排序) (2012-07-12 14:19:51) 转载▼ 标签: ...
- Java I/O流-ObjectInputStream、ObjectOutputStream
一.整体代码 ObjectStreamDemo.java import java.io.File; import java.io.FileInputStream; import java.io.Fil ...
- SVN库迁移
最后库迁移.机会主义的,在源库资源,然后上传到目标库,最后client更新url地址.的库被组长一眼识破,由于新库中没有大家的操作日志. 这次吸取上次的教训,用dump和load完毕SVN库迁移. 整 ...
- Android组件:Fragment切换后保存状态
之前写的第一篇Fragment实例,和大多数人一开始学的一样,都是通过FragmentTransaction的replace方法来实现,replace方法相当于先移除remove()原来所有已存在的f ...
- 手把手教你安装QT集成开发环境(操作系统为ubuntu10.04)
在安装QT集成开发工具包之前需要先安装build-essential和libncurses5-dev这两个开发工具和库,libncurses5-dev库是一个在Linux/Unix下广泛应用的图形函数 ...
- WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘
原文:WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘 通过<再谈IIS与ASP.NET管道>的介绍,相信读者已经对IIS和ASP.NET的请求处理管道有了一个大致 ...