LIS最长上升子序列详解+模板(dp和二分做法)
LIS
最长上升子序列(longest increasing subsequence),也可以叫最长非降序子序列,简称LIS
求LIS的长度的问题 是指求数列的最长上升子序列(可以不连续)的长度
dp做法 复杂度O(
)
我们用 dp[ i ]来代表以 a[ i ] 为结尾的前 i 个数最长递增子序列的长度
我们来看一下这个栗子 2 7 1 5 6 4 3 8 9 7 11 13 6 这个序列
首先 我们把所有位置的dp值初始化为1 (因为最小也是一 它本身就是一个递增长度为1 的序列)
我们拿a[ 7 ]这个位置举例 是8 我们找他前边所有小于8的数 有 2 7 1 5 6 4 3
然后从这些数的dp值中 找到最大的 是dp[ 4 ] = 3 (序列 2 5 6) dp[7]就等于这个最大的+1
方法就是 dp[ i ] 就等于 a[ i ] 前面比 a[ i ]小的所有的数 的dp值中 最大的那个 再+1
状态转移方程 dp[ i ] = max{ dp[ { a[ i ] 前面的数 } < a[ i ] ] } + 1
代码如下
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define maxn 1010
int a[maxn],dp[maxn];
int n,ans=1;
int main(){while(~scanf("%d",&n)){ans=0;for(int i=0;i<n;i++)cin>>a[i],dp[i]=1;for(int i=1;i<n;i++){for(int j=0;j<i;j++){if(a[j]<a[i])dp[i]=max(dp[j]+1,dp[i]);ans=max(ans,dp[i]);}}cout<<ans<<endl;}}
模拟+二分 复杂度〇(nlogn)
dp的做法复杂度是O(n^2) 显然这不够优秀
比如这道题就会超时 51nod1134
所以 我们用另一种求最长上升子序列的办法 复杂度是O(nlog(n))
他的做法是怎么样的呢 举个栗子
A数组 { 4 ,2 ,5 ,8 ,3 ,4 ,6 }
我们用一个B数组来存一个递增的序列 len时刻记录B数组长度
我们的原则是维护B数组的递增有序 在保持len尽量增大的前提下 使B中的值尽量的小
- A[0]=4 我们把A[1] 放入B[1] 同时 len=1
- A[1]=2 此时B数组中只有一个4 len=1 我们用2替换掉4 len依旧是2 B中的值也变小了
- A[2]=5 B中就一个2 len=1 我们把5加进去 B={2,5} len=2
- A[3]=8 8大于B数组中所有数 我们能使len增大就先让len增大 加进去 B={2,5,8} len=3
- A[4]=3 我们在B中找到刚好大于3的数 是5 那么用3替换5 值变小 且len不变 B={2,3,8}
- A[5]=4 仍然在B中找刚好大于4的数 是8 那么用4替换8 值变小 且len不变 B={2,3,4}
- A[6]=6 比B中都大 加进去 B={2,3,4,6} len=4
最终的答案是 len=4 也就是最长上升子序列长度为4
这个方法可以得到正确的a序列的LIS长度
但是最终在B数组中存的那个序列 并不是正确的LIS序列
比如 我们用这个方法找 A{ 4 ,2 ,5 ,8,3 } 的LIS长度时
那么用上边讲的前五个步骤就可以得到 len=3 但是B={2,3,8} 显然这并不是一个正确的序列
如果不明白为什么的话 再回去看看我们维护B数组时的原则 多看几遍 理解一下
这个原则能保证我们得到的B数组的len是正确的 并不能保证序列是正确的序列
由于B数组中的数列呢 一直是有序的 所以我们可以使用二分查找来维护B数组
从而使这个算法的复杂度降低到了 n*log(n)
下面给出我的写法
题目链接传送门51nod1134
两个写法
手打二分查找
代码
注意 这里和普通的二分有一点区别就是 普通二分是查找val 这里是查找大于等于val的值
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1e6 + 7;
int f[maxn], a[maxn];
int n;
//二分查找 查找小于等于x的数的位置
//如果找不到 返回r
int find(int l, int r, int x) {while (l < r) {int mid = (l + r) / 2;if (f[mid] < x) l = mid + 1;else r = mid;}return l;
}
int lis() {int len = 0;for (int i = 0; i < n; i++) {int k=find(0,len,a[i]);f[k] = a[i];if (k == len) len++;}return len;
}
int main() {scanf("%d", &n);for (int i = 0; i < n; i++) {scanf("%d", &a [i]);}printf("%d\n", lis());return 0;
}
STL lower_bound()
这个库函数会在 [ l,r)中二分查找 返回刚好大于等于val的值的地址 若没有 则返回 r 的地址
返回的都是地址 所以需要减去 首地址 才得到位置
代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+7;
int b[maxn],a[maxn];
int n;
int LIS(){int len=0;for(int i=0;i<n;i++){int k=lower_bound(b,b+len,a[i])-b;b[k]=a[i];if(k==len)len++;}return len;
}
int main(){scanf("%d",&n);for(int i=0;i<n;i++) scanf("%d",&a[i]);printf("%d",LIS());
}
变式 最大下降矩阵
河南CCPC省赛A题
题目描述
我们称一个矩阵是下降矩阵,当且仅当,矩阵的每一列都是严格下降的。很显然,这个要求很苛刻,大多数矩阵都无法满足。但是显然如果消去一些行,一定可以使得这个矩阵变成下降矩阵。
现在给出一个n行m列的矩阵,请你求出最少消去多少行,可以使得这个矩阵变为下降矩阵。
输入
输入第一行包含两个正整数n,m分别表示矩阵的行数和列数。(1<=n,m<=300)
接下来n行,每行有m个数,中间用空格隔开,每个数都小于2^31.输出
输出仅包含一个整数,即最少消去的行数。
样例输入 Copy
1 3 1 2 3样例输出 Copy
0
套用第一个n^2的求最长上升子序列的长度算法
只是那个是比较两个数的大小
改成比较两行的大小(一行数全部小于另一行对应的数,这行就小于另一行)
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=307;
int n,m;
int a[maxn][maxn];
int dp[maxn];
int cmp(int x,int y){for(int i=0;i<m;i++){if(a[x][i]>a[y][i]){return 0;}}return 1;
}
int main(){scanf("%d%d",&n,&m);for(int i=0;i<n;i++){for(int j=0;j<m;j++){scanf("%d",&a[i][j]);}}for(int i=0;i<n;i++)dp[i]=1;int ans=1;for(int i=1;i<n;i++){for(int j=0;j<i;j++){if(cmp(i,j))dp[i]=max(dp[j]+1,dp[i]);ans=max(ans,dp[i]);}}printf("%d\n",n-ans);return 0;
}
LIS最长上升子序列详解+模板(dp和二分做法)相关推荐
- LIS最长上升子序列详解(动态规划、贪心+二分、树状数组)
1.摘要: 关于LIS部分,本篇博客讲一下LIS的概念定义和理解,以及求LIS的三种方法,分别是O(n^2)的DP,O(nlogn)的二分+贪心法,以及O(nlogn)的树状数组优化的DP,最后附上几 ...
- Vue3 最长递增子序列详解
Vue3 最长递增子序列研究 本文初衷 彻底讲清楚 Vue3 源码中实现最长递增子序列的算法. 概念名词 **最长递增子序列:**在一个给定的数值序列中,找到一个子序列,使得这个子序列元素的数值依次递 ...
- LCS最长公共子序列和LIS最长上升子序列——例题剖析
一.LCS最长公共子序列 最长公共子序列(LCS)问题算法详解+例题(转换成LIS,优化为O(nlogn),看不懂你来打我) longest comment subsequence 模板题 longe ...
- 算法设计 - LCS 最长公共子序列最长公共子串 LIS 最长递增子序列
出处 http://segmentfault.com/blog/exploring/ 本章讲解: 1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度: 2. 与之类似但不 ...
- 详解模板注入漏洞(下)
作者 | 原作者gosecure,翻译整理shan66 来源 | http://gosecure.github.io/ 在上一篇文章中,我们为读者详细介绍了模版注入漏洞的概念,模版引擎的识别方法,以及 ...
- 最长公共子序列求序列模板提_最长公共子序列
最长公共子序列求序列模板提 Description: 描述: This question has been featured in interview rounds of Amazon, MakeMy ...
- python requests 异步调用_构建高效的python requests长连接池详解
前文: 最近在搞全网的CDN刷新系统,在性能调优时遇到了requests长连接的一个问题,以前关注过长连接太多造成浪费的问题,但因为系统都是分布式扩展的,针对这种各别问题就懒得改动了. 现在开发的缓存 ...
- java前端长连接框架_Java中Spring Boot+Socket实现与html页面的长连接实例详解
Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器 ...
- 详解模板引擎工作机制
本文讲的是详解模板引擎工作机制, 我已经使用各种模版引擎很久了,现在终于有时间研究一下模版引擎到底是如何工作的了. 简介 简单的说,模版引擎是一种可以用来完成涉及大量文本数据的编程任务的工具.一般而言 ...
- lis最长上升子序列o(nlogn)优化
LIS的暴力算法 我们知道,LIS(最长上升子序列,最长下降子序列,最长不上升子序列,最长不下降子序列)如果按照最初得方法做,我们设置的状态f[i]表示i结尾的最长LIS的长度,在枚举每一个数的时候都 ...
最新文章
- 理解Java动态代理(1)—找我还钱?我出钱要你的命
- 数据结构-图的深度优先遍历(DFS)和广度优先遍历(BFS)算法分析
- 修改linux bash shell PS1
- sdk和api有什么区别
- 使用CPU时间戳进行高精度计时
- Linux系统下文件与目录操作讲解
- ABAQUS后处理常用功能
- PTA 7-31 掉入陷阱的
- Jmeter进行SOAP协议接口性能测试
- 良树机器人_揭开坑王高屋良树不为人知的一面,你看过这部《冥王计划》吗?...
- Flink容错机制(一)
- QT界面优化---反走样
- 分块矩阵求行列式的一道题
- loadrunner入门教程(11)--回放脚本
- Gym - 101128H:Sheldon Numbers
- 项目实践:基于华为CCE环境下Tomcat的关键性能指标及监控方法
- Pycharm安装GDAL
- NPL系列之分词常用原则以及算法(三)
- REC_RE时延测量
- 顶级评委“天团”亮相,强势围观算法大咖争夺战