再探LIS
昨天讲课的时候突然想起来LIS还有一个东西没搞懂。
又去研究了下。
LIS问题就是要求一个序列中最长不下降或上升子序列,而此问题应用较广,例如很多题会有这样的条件
对于i,j如果他们可以同时选取,则必有|a[i]-a[j]| <= |b[i]-b[j]| (b[i]<b[j])
数学化归一下下得到 b[i]-b[j] <= a[i]-a[j] <= b[j]-b[i]
将i,j各放在一边 有
a[i]-b[i] >= a[j]-b[j]…………①,
a[i]+b[i] <= a[j]+b[j]…………②.
那么我们按其中一个条件排序,即保证排序后的序列对于任意的i,j(i<j)都有①式或②式成立,然后再按另一个条件求LIS即可求出最多可以取多少个。
若要知道取几次可以取完,那么 根据 dilworth 定理, 等同于求其反条件(即原来非降现在就是下降)子序列长度。
那么下面介绍求LIS的几种方法
法一 O(n^2)暴力dp转移
令dp[i]表示以ai结尾的最长不下降子序列长度
则转移方程为dp[i]=max{1,dp[j]+1} a[j]<=a[i]
则max{dp[i]}就是答案
法二 对于上面的dp转移,显然可以用树状数组优化
可以做到(nlogn)
设有 f[i] 表示以大小为i结尾的不下降子序列的最长长度
则dp[i]=max{f[j]}+1,j<=h[i];
max{f[j]}用树状数组维护
具体如下
int g() {
memset(C,,sizeof C);
int ans=;
for(int i=;i<=n;i++) {
dp[i]=;
int t=query(h[i])+;
ans=max(ans,t);
add(h[i],t);
}
return ans;
}
注意:1.可以不保留dp数组,直接更新ans即可。
2.如果max{h[i]}过大,则需要离散化
法三 二分法求LIS O(nlogn)
设s[i]表示以长度为i的上升子序列的最后一位的最小值。
则每次可以用a[i]去更新s[i]数组
显然s数组是不下降的
int h[Maxn],s[Maxn],ans,n;
int f(){
s[ans=]=-INF;
for(int i=;i<=n;i++){
int v=h[i];
if(v>s[ans])s[++ans]=v;
else*lower_bound(s+,s+ans+,v)=v;
}return ans;
}
对于第6行得到新的答案,根据s数组的定义,显然正确。
然后如果v<=s[ans],则此时不能更新ans,但是可以更新s数组,如何更新呢?跟上面一样,我们要找到一个s[i] (< v) 则此时可以用这个s[i]来更新s[i+1],而实际上只有这一个更新是有意义的,因为对于<=i的更新只会使答案更劣,对于i+2及以上的不能保证序列合法。
更新之后就会更新s[i+1]的值,又s[i]是最大的满足s[i]<v的i,所以s[i+1]必是最小的>=v的值,用lower_bound就好了。
同理如果要求非降子序列 改成下面这样即可
int h[Maxn],s[Maxn],ans,n=;
int f(){
s[ans=]=-INF;
for(int i=;i<=n;i++){
int v=h[i];
if(v>=s[ans])s[++ans]=v;
else*upper_bound(s+,s+ans+,v)=v;
}return ans;
}
可以发现改了两处,一是第6行的>= 二是lower_bound变成了upper_bound
请自行思考为什么这样可行
再探LIS的更多相关文章
- 【再探backbone 02】集合-Collection
前言 昨天我们一起学习了backbone的model,我个人对backbone的熟悉程度提高了,但是也发现一个严重的问题!!! 我平时压根没有用到model这块的东西,事实上我只用到了view,所以昨 ...
- ViewPager+Fragment再探:和TAB滑动条一起三者结合
Fragment前篇: <Android Fragment初探:静态Fragment组成Activity> ViewPager前篇: <Android ViewPager初探:让页面 ...
- 再探jQuery
再探jQuery 前言:在使用jQuery的时候发现一些知识点记得并不牢固,因此希望通过总结知识点加深对jQuery的应用,也希望和各位博友共同分享. jQuery是一个JavaScript库,它极大 ...
- [老老实实学WCF] 第五篇 再探通信--ClientBase
老老实实学WCF 第五篇 再探通信--ClientBase 在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创 ...
- Spark Streaming揭秘 Day7 再探Job Scheduler
Spark Streaming揭秘 Day7 再探Job Scheduler 今天,我们对Job Scheduler再进一步深入一下,对一些更加细节的源码进行分析. Job Scheduler启动 在 ...
- 再探ASP.NET 5(转载)
就在最近一段时间,微软又有大动作了,在IDE方面除了给我们发布了Viausl Studio 2013 社区版还发布了全新的Visual Studio 2015 Preview. Visual Stud ...
- 再探java基础——break和continue的用法
再探java基础——break和continue的用法 break break可用于循环和switch...case...语句中. 用于switch...case中: 执行完满足case条件的内容内后 ...
- 第四节:SignalR灵魂所在Hub模型及再探聊天室样例
一. 整体介绍 本节:开始介绍SignalR另外一种通讯模型Hub(中心模型,或者叫集线器模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用,对开发者来说相当友好. 该节包括的 ...
- 深入出不来nodejs源码-内置模块引入再探
我发现每次细看源码都能发现我之前写的一些东西是错误的,去改掉吧,又很不协调,不改吧,看着又脑阔疼…… 所以,这一节再探,是对之前一些说法的纠正,另外再缝缝补补一些新的内容. 错误在哪呢?在之前的初探中 ...
随机推荐
- javascript基础学习(七)
javascript之Object对象 学习要点: 创建Object对象 Object对象属性 Object对象方法 一.创建Object对象 new Object(); new Object(val ...
- 关于点击空白关闭弹窗的js写法推荐?
$(document).mouseup(function(e){ var _con = $(' 目标区域 '); // 设置目标区域 ){ // Mark 1 some code... // 功能代码 ...
- Linux之Vim编辑器使用
vim文本编辑器用于建立 编辑 显示文本文件,vim没有菜单,只有命令 在windows 平台下可使用gvim进行编写 Vim三种工作模式: 常有命令: 1.INSERT插入命令 i 在光标前插入 I ...
- js定位navigator.geolocation
一.简介 html5为window.navigator提供了geolocation属性,用于获取基于浏览器的当前用户地理位置. window.navigator.geolocation提供了3个方法分 ...
- Js冒泡事件和捕获事件
js中冒泡事件和捕获事件: 冒泡事件:冒泡事件是从里向外,即是从被绑定元素开始一直向外到达页面的所有祖先元素都会被触发,这 一过程被称为事件冒泡.这个事件从原始元素开始一直冒泡到DOM树的最上层 捕获 ...
- 用Raphael在网页中画圆环进度条
原文 :http://boytnt.blog.51cto.com/966121/1074215 条状的进度条我们见得太多了,实现起来比较简单,它总是长方形的,在方形的区域里摆 放就不太好看了.随着cs ...
- Sequoyah 本机开发Native Development: Invalid path for NDK(路径无效) 解决方案
打开window菜单下的preference选项.选择Android,Native Development(本地开发) 选择你的NDK安装目录. 但是,这个插件目前仅支持ndk的r4和r5版本,更高版 ...
- HDU2602 (0-1背包问题)
N - 01背包 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Descri ...
- iOS类别(Category)与扩展(Extension)-b
苹果的官方文档 Category在iOS开发中使用非常频繁.尤其是在为系统类进行拓展的时候,我们可以不用继承系统类,直接给系统类添加方法,最大程度的体现了Objective-C的动态语言特性. #im ...
- [BZOJ 1072] [SCOI2007] 排列perm 【状压DP】
题目链接:BZOJ 1072 这道题使用 C++ STL 的 next_permutation() 函数直接暴力就可以AC .(使用 Set 判断是否重复) 代码如下: #include <io ...