线性dp:最长上升子序列
1.常对象2.类为什么要传引用3.为什么要用const修饰某个对象?4.nth_element算法5.sprintf函数和sscanf函数6.scanf为什么比cin要快?7.矩阵旋转8.C++中的位运算9.std::ios::sync_with_stdio(0) 加速10.约数和倍数的性质11.对于质数的研究12.最大公约数和最小公倍数13.sort函数中的第三个参数:自定义排序方式14.排序15.大根堆和小根堆的介绍16.next_permutation17.线性dp:大盗阿福(打家劫舍)
18.线性dp:最长上升子序列
19.线性dp:最长公共子序列20.线性dp:最长公共子串21.宏定义define的用法22.算法比赛中常用的快读最长上升子序列
- 本文与leetcode300.最长递增子序列,这题题意一样,阅读完本文可以挑战一下
题目叙述:
给定一个无序的整数数组,找出其中最长上升子序列(LIS)的长度。
输入:
[5,7,1,9,4,6,2,8,3]
输出
4
解释
- 最长上升子序列是[1,4,6,8],其长度为4。
动态规划的设计:
-
首先,我们对数组进行下标的映射,我们从下标为1的位置开始计数
-
并且,我们计算以每一个元素a【i】为结尾的最长上升子序列的长度f【i】。
- 由上面这张图我们可知,最长的上升子序列可以由前面的状态推出,因此我们可以考虑使用动态规划算法
状态变量dp的含义:
- 我们在这里数组名用f,而不用dp
f[i]代表以a[i]为结尾的最长上升子序列长度
- 初始条件为f[i]=1
递推公式:
- 我们需要一个j指针,每次遍历到a【i】时,我们都需要j指针,从1开始移动,移动到i-1的位置,如果发现a[i]>a[j],那么我们就更新f【i】的值,不过还得判断一下f【j】+1与f【i】的大小关系。
for(int i=2;i<=n;i++){ for(int j=1;j<i;j++){ if(a[i]>a[j]) f[i]=max(f[i],f[j]+1); } //ans为最终的最大上升子序列的长度 ans=max(ans,f[i]); }
遍历顺序:
- 因为我们的状态变量
f[i]
是由f[j]
决定的,所以说我们的遍历顺序显然是从前向后遍历
如何初始化?
- 在上面已经说了:
f[i]=1
,因为每个数字至少都有以本身为序列的最长上升子序列。
举例打印dp数组
-
下标 :1,2,3,4,5,6,7,8,9
-
a【i】:5,7,1,9,4,6,2,8,3
-
f【i】 :1,2,1,3,2,3,2,4,3
递推式满足的条件
- 由小推大(最优子结构)
- 由过去推现在(无后效性)
疑问:
- f【i】记录以a【i】为开头的最长上升子序列可以吗?——可以,不过遍历顺序就是从后向前遍历,递推式也需要改变改变,这里读者可以自行推理!
- f【i】记录前i个数的最长上升子序列的长度,可以吗?——不可以,举例两个就可以发现明显错误的反例,因此不成立。
代码实现:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=1010; int n, a[N]; int f[N]; int main(){ cin>>n; for(int i=1; i<=n; i++) cin>>a[i]; for(int i=1; i<=n; i++) f[i]=1; for(int i=1; i<=n; i++) for(int j=1; j<i; j++) if(a[j]<a[i]) f[i]=max(f[i],f[j]+1); int res=0; for(int i=1; i<=n; i++) res=max(res,f[i]); cout<<res; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战