动态规划——最长上升子序列模型
最长上升子序列
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N。
第二行包含 N 个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N≤1000
−109≤数列中的数≤109
按照往常方法分析状态转移方程和集合表示。
给如一般代码如下:
普通版
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 1010;
int f[N],g[N];
int n;
int main()
{ios::sync_with_stdio(false);cin.tie(0);int ans = 0;cin >> n;for (int i = 1; i <= n; i++)cin >> g[i];for (int i = 1; i <= n; i++){f[i] = 1;for (int j = 1; j <= i - 1; j++){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){ans= max(ans, f[i]);}cout << ans;return 0;}
优化版:
利用单调栈进行优化
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 1e5 + 10;
int a[N];
int n;
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin >> n;for (int i = 1; i <= n; i++)cin >> a[i];vector<int>stk;stk.push_back(a[1]);for (int i = 1; i <= n; i++){if (a[i] > stk.back()){stk.push_back(a[i]);}else{*lower_bound(stk.begin(), stk.end(), a[i]) = a[i];}}cout << stk.size();return 0;
}
例题1:怪盗基德的滑翔翼
怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。
而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。
有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。
不得已,怪盗基德只能操作受损的滑翔翼逃脱。
假设城市中一共有N幢建筑排成一条线,每幢建筑的高度各不相同。
初始时,怪盗基德可以在任何一幢建筑的顶端。
他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。
因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。
他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。
请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?
输入格式
输入数据第一行是一个整数K,代表有K组测试数据。
每组测试数据包含两行:第一行是一个整数N,代表有N幢建筑。第二行包含N个不同的整数,每一个对应一幢建筑的高度h,按照建筑的排列顺序给出。
输出格式
对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。
数据范围
1≤K≤100
1≤N≤100
0<h<10000
输入样例:
3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10
输出样例:
6
6
9
例1分析:考虑到我们不能确定基德飞的方向,所以需要从前往后求一遍lcs,和从后往前求一遍lcs,基本思路和模板相同。代码如下
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 110;
int k, n;
int g[N], f[N];
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin >> k;while (k--){cin >> n;for (int i = 1; i <= n; i++){cin >> g[i];}int res = 0, ans = 0;for (int i = 1; i <= n; i++){f[i] = 1;for (int j = 1; j <= i - 1; j++){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){res = max(res, f[i]);}memset(f, 0, sizeof f);for (int i = n; i >= 1; i--){f[i] = 1;for (int j = n; j >= i + 1; j--){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){ans = max(ans, f[i]);}cout << max(ans, res) << endl;}return 0;
}
例题2:登山
五一到了,ACM队组织大家去登山观光,队员们发现山上一共有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。
同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。
队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?
输入格式
第一行包含整数N,表示景点数量。
第二行包含N个整数,表示每个景点的海拔。
输出格式
输出一个整数,表示最多能浏览的景点数。
数据范围
2≤N≤1000
输入样例:
8
186 186 150 200 160 130 197 220
输出样例:
4
例2分析:
考虑到一旦下山就不能再上山,那么为了保证浏览最多的景点,那么方案一定是一段先递增后递减的山峰,为了保证总体最大,可以开一个数组存放从前往后遍历的值,再从后往求一段lcs即可,同时取max即可。给出代码如下,特别需要注意的是每个点再计算自己时都被用到了两次所以需要减去1(因为这debug了好一会);
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 1010;
int f[N], g[N],a[N];
int n;
bool flag;
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin >> n;int res = 0;for (int i = 1; i <= n; i++){cin >> g[i];}for (int i = 1; i <= n; i++){f[i] = 1;for (int j = 1; j <= i - 1; j++){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){a[i] += f[i];}memset(f, 0, sizeof f);for (int i = n; i >= 1; i--){f[i] = 1;for (int j = n; j >= i + 1; j--){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){a[i] += f[i];res = max(res, a[i]);}//这里需要减一的原因是从前往后和从后往前数,当前点被计算了两次。cout << res-1;}
例题3:合唱队形
N 位同学站成一排,音乐老师要请其中的 (N−K)(N−K) 位同学出列,使得剩下的 KK 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 KK 位同学从左到右依次编号为 1,2…,K他们的身高分别为 T1,T2,…,TK 则他们的身高满足 T1<…<Ti>Ti+1>…>TK(1≤i≤K)>。
你的任务是,已知所有 N 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
输入的第一行是一个整数 N,表示同学的总数。
第二行有 N 个整数,用空格分隔,第 i 个整数 Ti 是第 i 位同学的身高(厘米)。
输出格式
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
数据范围
2≤N≤100
130≤Ti≤230
输入样例:
8
186 186 150 200 160 130 197 220
输出样例:4
正在上传…重新上传取消
例3分析:
和前面的登山问题类似,这里仍然可以用从前往后和从后往前的总最大值求组,用n减去(res-1)即可,给出ac码如下。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 110;
int f[N], g[N],a[N];
int n;
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin >> n;int res = 0;for (int i = 1; i <= n; i++){cin >> g[i];}for (int i = 1; i <= n; i++){f[i] = 1;for (int j = 1; j <= i - 1; j++){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){a[i] += f[i];}memset(f, 0, sizeof f);for (int i = n; i >= 1; i--){f[i] = 1;for (int j = n; j >= i + 1; j--){if (g[j] < g[i]){f[i] = max(f[i], f[j] + 1);}}}for (int i = 1; i <= n; i++){a[i] += f[i];res = max(res, a[i]);}cout << n - res + 1;return 0;}
例4:最大上升子序列和
一个数的序列 bi,当 b1<b2<…<bSb1<b2<…<bS 的时候,我们称这个序列是上升的。
对于给定的一个序列(a1,a2,…,aNa1,a2,…,aN),我们可以得到一些上升的子序列(ai1,ai2,…,aiKai1,ai2,…,aiK),这里1≤i1<i2<…<iK≤N1≤i1<i2<…<iK≤N。
比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。
这些子序列中和最大为18,为子序列(1,3,5,9)的和。
你的任务,就是对于给定的序列,求出最大上升子序列和。
注意,最长的上升子序列的和不一定是最大的,比如序列(100,1,2,3)的最大上升子序列和为100,而最长上升子序列为(1,2,3)。
输入格式
输入的第一行是序列的长度N。
第二行给出序列中的N个整数,这些整数的取值范围都在0到10000(可能重复)。
输出格式
输出一个整数,表示最大上升子序列和。
数据范围
1≤N≤1000
输入样例:
7
1 7 3 5 9 4 8
输出样例:
18
例4分析:本题可以和最长上升子序列相联系,考虑到求取最长上升子序列时我们的转移方程f[i]取的是1,那么这里的权值改成题目中所给即可,不要忘记转移方程是也要修改,代码如下。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int>PII;
const int N = 1010;
int n;
int g[N], f[N];
int main()
{ios::sync_with_stdio(false);cin.tie(0);int res = 0;cin >> n;for (int i = 1; i <= n; i++) cin >> g[i];for (int i = 1; i <= n; i++){f[i] = g[i];for (int j = 1; j <= i - 1; j++){if (g[j] < g[i]){f[i] = max(f[i], f[j] + g[i]);}}}for (int i = 1; i <= n; i++){res = max(res, f[i]);}cout << res;return 0;
}
动态规划——最长上升子序列模型相关推荐
- 【动态规划专题】最长上升子序列模型
题目 算法 A.AcWing 895. 最长上升子序列 模板 B.AcWing1017. 怪盗基德的滑翔翼 最长上升子序列/最长下降子序列 C.AcWing 1014. 登山 最长上升子序列/最长下降 ...
- ACwing 895 - 最长上升子序列(最长上升子序列模型)
给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少. 输入格式 第一行包含整数N. 第二行包含N个整数,表示完整序列. 输出格式 输出一个整数,表示最大长度. 数据范围 1 ≤ N ≤ ...
- [AcWing] 1012. 友好城市(C++实现)最长上升子序列模型、较为特殊
[AcWing] 1012. 友好城市(C++实现)最长上升子序列模型 1. 题目 2. 读题(需要重点注意的东西) 3. 解法 4. 可能有帮助的前置习题 5. 所用到的数据结构与算法思想 6. 总 ...
- 动态规划—最长公共子序列问题 HDU-1159 Common Subsequence
动态规划-最长公共子序列问题 Common Subsequence [ HDU - 1159 ] A subsequence of a given sequence is the given sequ ...
- 详解动态规划最长公共子序列--JavaScript实现
前面两篇我们讲解了01背包问题和最少硬币找零问题.这篇将介绍另一个经典的动态规划问题--最长公共子序列.如果没看过前两篇,可点击下面链接. 详解动态规划最少硬币找零问题--JavaScript实现 详 ...
- 动态规划1--最长公共子序列
动态规划1--最长公共子序列 一.动态规划 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并 综合子问题的解导出大问题的解的方法,问题求解耗时 ...
- 动态规划——最长上升子序列问题(LIS)
动态规划--最长上升子序列问题(LIS) 最长上升子序列问题(LIS).给定n个整数A1,A2,-,AnA_1, A_2, \dots , A_n,按从左到右的顺序选出尽量多的整数,组成一个上升子序列 ...
- 最长公共子序列php,动态规划(最长公共子序列LCS)
概念 求解决策过程最优化的结果 (可能有多个) 把多阶段过程转化为一系列单阶段过程,利用各阶段之间的关系,逐个求解 计算过程中会把结果都记录下,最终结果在记录中找到. 举例 求两个字符串的最长公共子序 ...
- 最长上升子序列模型 AcWing 1010. 拦截导弹
最长上升子序列模型 AcWing 1010. 拦截导弹 原题链接 AcWing 1010. 拦截导弹 算法标签 DP 线性DP 最长上升子序列 思路 摘自该题解 代码 #include<bits ...
最新文章
- Composer 篇
- 自学成才翁_如何发挥自学成才的内在游戏
- java中打开指定的文件夹
- int类型存小数 mysql_2020年最新版MySQL面试题(一)
- Dict Set usage
- TransactionScope 分布式事务
- java ftp分片续传_Java写的支持断点续传的FTP
- javascript系列之执行上下文
- print函数python_带有结束参数的Python print()函数
- python list 底层_深入Python列表的内部实现
- 恒大集团:网传《关于召开全国恒大财富投资人大会通知》系伪造
- azure 安全组_具有安全性和设计注意事项的Azure成本跟踪
- [BZOJ4810][Ynoi2017]由乃的玉米田 莫队+bitset
- 短视频图像处理 OpenGL ES 实践
- Linux中tmux多终端复用和Screen多窗口工具
- 11、权重残差图、RLE和NUSE
- 北京迷笛音乐节阵容、北京草莓音乐节阵容
- 微信小程序幽默风和git
- xshell6使用技巧_Xshell 6怎么设置为中文-Xshell 6设置为中文的方法 - 河东软件园
- java连接池详解与自定义es连接池
热门文章
- python软件设计背景_教你用Python制作微信好友背景墙
- 字节码(.class)文件解读
- 可视化神器Plotly玩转柱状图
- c语言 char 追加,如何在C中的char数组末尾添加char(零)?
- el java常量_java - EL
- 关于html5的video标签不能播放视频的问题及解决方法
- CRMEB新零售社交会员管理营销电商系统!
- android高德地图截屏,ios用截图方法截取高德地图变成空白
- CUDA2.4-原理之性能优化及浮点运算
- 快递查询API(最新)