简单DP入门(二) 最长上升子序列及其优化
最长上升子序列解决问题:
有N个数,求出它最长的上升子序列并输出长度。
在题里不会讲的这么直白,这个算法往往会与其他的算法混在一起使用。
在这篇文章中不会出现其他的例题,为了让大家更好的理解,我只会对模板进行讲解。(谢谢大家的理解)
1-朴素算法(时间复杂度炒鸡炒鸡高)
首先,我们先列出一些无序的数进行观察,例如:1 7 4 2 3 6 8 9 (共8个数)。
我们通过观察很快可以发现在这个序列中最长的上升序列时1,2,3,6,8,9,长度为6,我们可以把每种情况都遍历一遍,我们现在从1开始,当1为子序列第一个元素的时候,我们1后面比1大的数:7,4,2,3,6,8,9,然后我们一个一个试,如果下一个选择7,那么在寻找7后面比7大的数:8,9,我们选择8,然后再寻找8后面比8大的数9,然后我们选择9,此时后面已经没有元素了,记录下此时的最长上升子序列:1,7,8,9(长度为4),然后回到1,我们已经选择过7了,现在换成4,继续前面的操作,找到序列1,4,6,8,9(长度为5),比原来的4大,所以最长上升子序列更新为5,按照这样的操作,我们最终扫完后就可以得到了最长上升子序列:6;
这种算法是最容易想的朴素算法,平淡无奇,理所当然,但是不动脑子的代价就是慢!!!(况且也不一定好打)
我也不上代码了,相信上了大家也不会看(正好也不用打了)。
2-动态规划V1.0(时间复杂度O(n^2))
现在进入正题,最长上升子序列的动态规划做法是目前最普遍的,较简便的做法,在对时间复杂度不是特别高的时候,大部分人都会选择这种方法,因为它很好理解也好打。
首先,作为DP,那么久要有两个要素:状态和转移方程。考虑到看这篇文章的通常都是动态规划的初学者,我在这里提一下动态规划的基本思想:将一个大问题分成多个小问题,通过对很好解决的小问题的求解,得出初始的状态,可以用动态规划解决的题目一般都能找出来从一个状态转移到另一个状态的方法,称为状态转移方程,一般从简单到复杂,数据范围从小到大进行转移,最终通过转移的结果得出最终的最优解往往就是答案(某些数位DP除外),动态规划简单来说就是用各阶段的最优解来推导下一阶段的最优解,相似于记忆化搜索,但是二者又不尽相同。为什么动态规划快呢,因为它减少了很多的不必要的重复搜索过程,以达到剪枝的效果。
我们想要进行动态规划,要先设出状态,动态规划题中的状态需要和答案有密切的联系,并且可以利用你设的状态推导出转移方程。
最长上升子序列的问题顾名思义是最长上升子序列的长度,所以我们的f[i]=最长的长度(在动态规划题中习惯用f[]或者dp[]来表示状态数组)而这个i表示什么呢,本题也没什么东西了,只有一串数,那么这个i就表示以第i个数结尾吧。这样我们的状态就出来了,f[i]表示以第i个数字结尾的最长上升子序列的长度。答案输出f[N]即可。
状态出来了,我们就开始推导转移方程:在推导时我们需要关注的只是这一阶段和它的上一阶段之间的联系(可千万不要往深了想,很可能把自己绕晕),f[i]是以第i个数为结尾的,它的上一阶段只有可能是f[j](其中1<=j<=i-1) 因为f[j]表示的是结尾为第j个数的最长上升子序列的长度,所以在从f[j]推导到f[i]时我们只需考虑是否第j个数小于第i个数,如果小于那么f[i]=f[j]+1(从第j个数为结尾转换成以第i个数为结尾多了一个数,所以长度加1),因为不知道1到i-1中哪个最合适,就都便历一边,最后保留最长的f[i]即可完成这一状态的转移。书写出来就是f[i]=max(f[i],f[j]+1);(max指的是从两个数中选出最大的返回,如果这一状态转移过后还没有原来的f[i]长,那么肯定不转移了呀)。
现在状态和转移方程都出来了,那么我可以上代码了。(代码十分的好理解)
Code:
#include<iostream>
#include<cstdio>
using namespace std;
int f[]={};
int n,a[]={},ans=;
int main(){
cin>>n;
for(int i=;i<=n;i++){
cin>>a[i];
}
for(int i=;i<=n;i++){
f[i]=;
for(int j=;j<i;j++){
if(a[j]<a[i]){
f[i]=max(f[i],f[j]+);
}
}
ans=max(f[i],ans);
}
cout<<ans;
}
没啥难以理解的。
3-动态规划V2.0(时间复杂度O(NlogN))
为什么我画了一条线呢?
主要是为了与前面那些笨拙的算法区别开,这种算法比较巧,而且实用。
在介绍这种算法之前呢,我要先介绍一个C++STL中函数lower_bound(),具体用法附上百度百科的链接https://baike.baidu.com/item/lower_bound/8620039?fr=aladdin
相信大家都看懂了,下面我来讲下这个动态规划+二分优化的基本思想:我们定义一个数组f[i]这里面的i和上一个1.0的i可不一样了,这个i表示长度为i的最长上升子序列。而f[i]则表示长度为i的子序列的最小结尾。是不是没看懂,那让我来举个例子:例如有1,7,4,2,3,6,8,9 八个数,f[1]=1,因为长度为1的最长上升子序列中只含有一个数1,所以最小一定是1,接着我们看f[2],7比1大,可以将它加入,那么此时i=2,f[2]=7,因为此时的序列是1,7,以7结尾当然7是最小结尾,接着4<7所以我们将4替换到它第一个可以插入的不影响序列顺序的位置,与那个位置上比它大的数交换,更新该位上的f。比如这时,4比1大,比7小,所以应该和7交换,将队列变成1,4,i不变(因为队列长度还是2),所以我们这个算法的思想就是拼命维护长度为i的序列有最小的结尾,这样它就可以在后面容纳更多的数。接着,2>4,所以2将4替换掉,序列变成1,2。i仍然不变化,继续向下推,3小于2,所以i++,将3加入序列最后,此时序列为1,2,3,i=3。6>3,所以加入6,i=4。8>6所以将8加入序列,i=5。接着加入9,i=6。此时已经到了结尾,退出循环,我们看,此时的i就是最长上升子序列的长度了。
Code:
#include<iostream>
#include<cstdio>
using namespace std;
int f[]={},len;
int n,a[]={},ans=;
int main(){
cin>>n;
for(int i=;i<=n;i++){
cin>>a[i];
}
for(int i=;i<=n;i++){
if(a[i]>f[len]) f[++len]=a[i];
else if(a[i]<f[len]){
*lower_bound(f+,f+len+,a[i])=a[i];
}
}
cout<<len;
}
我们会发现,这种方法和贪心有些像,所以这种方法也可以说是动态规划+贪心优化(动态规划V2.0)。
好了,这篇文章到这里也该结束了,反正我这么弱,大家看看就好。
简单DP入门(二) 最长上升子序列及其优化的更多相关文章
- HDU 1159 Common Subsequence --- DP入门之最长公共子序列
题目链接 基础的最长公共子序列 #include <bits/stdc++.h> using namespace std; ; char c[maxn],d[maxn]; int dp[m ...
- POJ_2533 Longest Ordered Subsequence【DP】【最长上升子序列】
POJ_2533 Longest Ordered Subsequence[DP][最长递增子序列] Longest Ordered Subsequence Time Limit: 2000MS Mem ...
- HDU 2084 数塔(简单DP入门)
数塔 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- 【dp】求最长上升子序列
题目描述 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.我们想知道此时最长上升子序列长度是多少? 输入 第一行一个整数N,表示我们要将1到N插入序列中 ...
- hdu1243 dp (类最长公共子序列)
题意:射击演习中,已知敌人出现的种类顺序,以及自己的子弹种类顺序,当同种类的子弹打到同种类的敌人时会得到相应分数,问最多能得多少分. 这题的题意很好理解,而且模型也很常见,是带权值的类最长公共子序列问 ...
- hdu1080 DP(类最长公共子序列)
题意,有两个字符串,分别由四个字母构成,字母之间有不同的相似度,允许在两个字符串都按原顺序排列的情况下进行字母与字母之间的匹配,也可以让字母与空格匹配,即相当于在字符串中间加空格来一一匹配,每个字母与 ...
- POJ 1458 Common Subsequence (DP+LCS,最长公共子序列)
题意:给定两个字符串,让你找出它们之间最长公共子序列(LCS)的长度. 析:很明显是个DP,就是LCS,一点都没变.设两个序列分别为,A1,A2,...和B1,B2..,d(i, j)表示两个字符串L ...
- BZOJ 1207 [HNOI2004]打鼹鼠:dp【类似最长上升子序列】
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1207 题意: 有一个n*n的网格,接下来一段时间内会有m只鼹鼠出现. 第i只鼹鼠会在tim ...
- DP专辑之最长公共子序列及其变形
vijos1111(裸的最长公共子序列) 链接:www.vijos.org/p/1111 题解:好久没有写最长公共子序列了,这题就当是复习了.求出最长公共子序列,然后用两个单词的总长度减去最长公共子序 ...
随机推荐
- vue.js2.0 (简易)水果商城 vuex vant-ui
vue.js2.0 (简易)水果商城 vuex vant-ui:https://segmentfault.com/a/1190000015690250 vue2.5全家桶 高仿vivo商城 百分之95 ...
- uploadify多图片和文件上传网站应用
先要下载压缩包 www.uploadify.com/wp-content/uploads/files/uploadify.zip 1,模板文件引用 <!--引用jquery uploady*}- ...
- DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法
原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...
- mysql的一些基本常识
1.主键的选取 主键的字段不能有null存在 主键应该使用bigint自增,而不是int 主键的选取默认为id 联合主键:就是多个字段被设置为主键,这里主键字段的值是允许相同的,只要不是所有字段相同即 ...
- BZOJ-2337 XOR和路径(HNOI2011)概率DP+概率的线性叠加
题意:给出n个点和m条边,每条边有权值wi,从1出发,每次等概率选一条出边走,直到终点n停止,得到的值是路径所有边的异或和.问异或和期望. 解法:这道题非常有意思!首先比较直观的想法就是dp[x]代表 ...
- windows下搭建Mongo主(Master)/从(slave)数据库同步
需要启动两个mongoDb文档数据库,一个是主模式启动,另一个是属于从模式启动. 1. 创建主从服务器 主服务器:192.168.1.131:27017 备服务器:192.168.1.131:2701 ...
- ivew url 的输入
1. <FormItem label="链接" prop="url"> <Input v-model="formValidate.u ...
- 苹果的AR赌注仍然有很多需要证明的
苹果公司为开发者主题发布会做准备,其中一个更大的公告很可能是其增强现实平台的新变化.自从去年宣布ARKit以来,这家科技巨头几乎对其对AR的潜力抱有信心. 在很多讨论背后,人们都相信技术的实用性,但在 ...
- [BZOJ5428][九省联考2018]双木棋
去年觉得高不可攀的题啊... 貌似就很沙茶了QAQ 直接状压每一行是多少然后合法状态是LIS状态数极少所以随便dp一下就好了啊... 注意初值啥的得赋对才行QAQ 我菜死了 //Love and Fr ...
- GSL+DevC++使用
在DEV C++中配置GSL1.8库 前面写了如何在vs2005中添加gsl,本文所所述为在dev c++中使用gsl库,由实践总结而得. 准备软件: 1.Orwell Dev C++ 5.6.2 N ...