动态规划之最长上升子序列模型

动态规划分为很多模型,比如说数字三角形模型,最长上升子序列模型,背包模型,状态机模型,状态压缩,区间dp,树形dp等等
下面,我就Acwing提高课中,最长上升子序列模型进行了整理。

涉及知识点总览

主要涉及的算法知识点有:

  • 最长上升子序列 O(N2)O(N^2)O(N2)O(NlogN)O(NlogN)O(NlogN)求解方法
  • 正反双向 LIS
  • 最大上升子序列和
  • 偏序集-Dilworth定理的两种证明

这里补充一个 O(N2)O(N^2)O(N2) dp 数组的优化
f[i] 代表的是 以 i 为结尾的最长上升子序列的长度,他需要 f[i-1] …f[i-2] … 的信息来更新自己
参考网上思路,发现可以用树状数组。用数值做下标,维护长度最大值,从后往前循环,每次查询之前已经放到树状数组里面的数中以这个数结尾的最长不上升子序列的长度的最大值,然后把这个最大值+1作为以自己结尾的最长不上升子序列的长度,放到树状数组里面

这里我们简单温习一下树状数组:

一、怪盗基德的滑翔翼

题目链接如下 怪盗基德的滑翔翼
在这里插入图片描述
根据题目的描述信息可知,该题目就是要求对于某一个建筑,往 左 能够最长下降子序列的长度,或者是往 右的租场下降子序列的长度,因此有两种解法一种是我们常见的dp最长上升子序列,还有一种就是贪心最长上升子序列。
下面,是我给出的两种解法,具体的解释都放在了代码里面

#include <bits/stdc++.h>
using namespace std;const int N = 110;
int f[N], a[N], g[N];
int ans, n;/*dp LISf[i] = f[j] + 1 (j < i && a[j] < a[i])正方跑两次
*/
void sol1() {int ans = 0;for (int i = 1; i <= n; i ++ ) {f[i] = 1;for (int j = 1; j < i; j ++ ) {if (a[j] < a[i]) {f[i] = max(f[i], f[j] + 1);}}ans = max(ans, f[i]);}for (int i = n; i >= 1; i -- ) {g[i] = 1;for (int j = n; j > i; j -- ) {if (a[j] < a[i]) {g[i] = max(g[i], g[j] + 1);}}ans = max(ans, g[i]);}printf("%d\n", ans);
}/*使用最长上升子序列模型的贪心解法f[i] 表示长度为 i 的最小末尾不难得知,f[i] 数组应该是单调递增的(相等的情况都不会出现)对于每次循环 数组 a 的元素,都是将 寻找小于 a[i] 的最大 f[j] 的位置,然后更新f[j + 1]初始化数组memset(f, 0x3f, sizeof f), f[0] = 0;
*/
void sol2() {int len = 1, ans = 0;memset(f, 0x3f, sizeof f);f[0] = 0;f[1] = a[1];for (int i = 2; i <= n; i ++ ) {static int l, r, mid;l = 0, r = len;while (l < r) {mid = l + r + 1 >> 1;   // 注意这里的 + 1if (f[mid] < a[i]) {l = mid;} else {r = mid - 1;}}f[l + 1] = a[i];len = max(len, l + 1);  // 更新 len}ans = max(ans, len);len = 1;memset(f, 0x3f, sizeof f);f[0] = 0;f[1] = a[n];for (int i = n - 1; i >= 1; i -- ) {static int l, r, mid;l = 0, r = len;while (l < r) {mid = l + r + 1 >> 1;   // 注意这里的 + 1if (f[mid] < a[i]) {l = mid;} else {r = mid - 1;}}f[l + 1] = a[i];len = max(len, l + 1);}ans = max(ans, len);printf("%d\n", ans);
}int main() 
{int T;  cin >> T;while (T -- ) {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);}// sol1();sol2();}return 0;
}

二、登山

Acwing 登山题目链接
在这里插入图片描述
该登山题目和原本的 怪盗基德 问题有些类似,但是又有些不同,他们都需要正反两次求出最长上升子序列,但是不同的是怪盗基德可以使用 nlog(n) 的贪心算法,因为他不必在意于最后的结尾是哪个点。
但是,登山问题是需要知道 该点的位置是在哪里的。
因此登山问题必须要使用 O(N2)O(N^2)O(N2)的dp求解,
f[i] 表示以i 为结尾的最长上升子序列

#include <bits/stdc++.h>
using namespace std;const int N = 1010;
int f[N], a[N], g[N];
int ans, n;/*dp LISf[i] = f[j] + 1 (j < i && a[j] < a[i])正方跑两次
*/
void sol1() {int ans = 0;for (int i = 1; i <= n; i ++ ) {f[i] = 1;for (int j = 1; j < i; j ++ ) {if (a[j] < a[i]) {f[i] = max(f[i], f[j] + 1);}}}for (int i = n; i >= 1; i -- ) {g[i] = 1;for (int j = n; j > i; j -- ) {if (a[j] < a[i]) {g[i] = max(g[i], g[j] + 1);}}}for (int i = 1; i <= n; i ++ ) {ans = max(ans, f[i] + g[i] - 1);    // 注意这里的 - 1}printf("%d\n", ans);
}int main() 
{int T;  T = 1;while (T -- ) {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);}sol1();}return 0;
}

三、合唱队形

Acwing 合唱队型
在这里插入图片描述
本题就是换了一种问法,最少有多少同学出列,就是需要求出我们的最长上升子序列,和我们的登山问题是一个性质,也是只可以使用O(N2)O(N^2)O(N2)算法进行求解

#include <bits/stdc++.h>
using namespace std;const int N = 1010;
int f[N], a[N], g[N];
int ans, n;/*dp LISf[i] = f[j] + 1 (j < i && a[j] < a[i])正方跑两次
*/
void sol1() {int ans = 0;for (int i = 1; i <= n; i ++ ) {f[i] = 1;for (int j = 1; j < i; j ++ ) {if (a[j] < a[i]) {f[i] = max(f[i], f[j] + 1);}}}for (int i = n; i >= 1; i -- ) {g[i] = 1;for (int j = n; j > i; j -- ) {if (a[j] < a[i]) {g[i] = max(g[i], g[j] + 1);}}}for (int i = 1; i <= n; i ++ ) {ans = max(ans, f[i] + g[i] - 1);    // 注意这里的 - 1}printf("%d\n", n - ans);
}int main() 
{int T;  T = 1;while (T -- ) {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);}sol1();}return 0;
}

四、友好城市

Acwing 友好城市链接
在这里插入图片描述
对于本题而言,他所求的不相交的航线,就是我们排序之后的不递减子序列
排序,可以按照是左岸排序,也可以按照右岸进行排序

/*
这个题目个关键在于,你连一连这个友好城市的航线,你就会发现
航线不相交,就是排序之后他们的那个最长上升子序列
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
int n, f[N], len;class Node {
public:int x, y;Node() {//}Node(int x, int y): x(x), y(y) {//}}a[N];
bool cmp1(const Node &t1, const Node &t2) {if (t1.x ==  t2.x) {return t1.y <= t2.y;} else {return t1.x < t2.x;}
}
bool cmp2(const Node &t1, const Node &t2) {if (t1.y ==  t2.y) {return t1.x <= t2.x;} else {return t1.y < t2.y;}
}int sol1() {sort(a + 1, a + n + 1, cmp1);// sort(a + 1, a + n + 1, cmp2);// 求解的是最长非递减子序列/*因此变成了寻找 f[i] 中 小于等于 val 的最大位置*/int val;memset(f, 0x3f, sizeof f);f[0] = 0;len = 0;for (int i = 1; i <= n; i ++ ) {val = a[i].y;static int l, r, mid;l = 0, r = len;while (l < r) {mid = l + r + 1 >> 1;if (f[mid] <= val) {    // 注意这里是 小于等于l = mid;} else {r = mid - 1;}}f[l + 1] = val;len = max(len, l + 1);}cout << len << endl;
}int sol2() {sort(a + 1, a + n + 1, cmp2);// 求解的是最长非递减子序列/*因此变成了寻找 f[i] 中 小于等于 val 的最大位置*/int val;memset(f, 0x3f, sizeof f);f[0] = 0;len = 0;for (int i = 1; i <= n; i ++ ) {val = a[i].x;static int l, r, mid;l = 0, r = len;while (l < r) {mid = l + r + 1 >> 1;if (f[mid] <= val) {    // 注意这里是 小于等于l = mid;} else {r = mid - 1;}}f[l + 1] = val;len = max(len, l + 1);}cout << len << endl;
}int main() 
{cin >> n;for (int i = 1; i <= n; i ++ ) {scanf("%d%d", &a[i].x, &a[i].y);  }//sol1();sol2();return 0;
}

五、最大上升子序列和

Acwing 最大上升子序列和链接
在这里插入图片描述
原本的最长上升子序列,有两种求解方法一个是 dp 求解,一个是类似于折半查找的贪心求解,
本题的最大上升子序列和,能否也用这两种方法呢? dp求解是显然可以使用的,贪心求解的话,我们尽心探讨

LIS可以使用贪心求解,是因为 f[i] 数组代表 长度 为 i 的最小末尾,然后利用该数组的单调特性,进行优化,Nlog(N)Nlog(N)Nlog(N)
但是,此时的 f[i] 数组倘若代表的是 和为 i, 的最小末尾,首先存不下,而且也没有单调性可言(主要是因为 len是连续的,但是和不是连续的,len是有0,1,2, 3, 4, 5,但是和的话是跳跃性的没办法),所以无法进行类似的优化,还是得 O(N2)O(N^2)O(N2)的算法

#include <bits/stdc++.h>
using namespace std;const int N = 1010;
int n, f[N];
int a[N];int main() 
{cin >> n;for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);}memset(f, 0, sizeof f);int ans = 0;for (int i = 1; i <= n; i ++ ) {f[i] = a[i];for (int j = 1; j < i; j ++ ) {if (a[j] < a[i]) {f[i] = max(f[i], f[j] + a[i]);}}ans = max(ans, f[i]);}cout << ans << endl;return 0;
}

六、拦截导弹

Acwing 拦截导弹链接
在这里插入图片描述

首先 不难得知,一套系统最多拦截的导弹数目 那么就是最长非递增子序列

最少使用多少套系统,最少有多少个 非递增子序列
他的个数就等于 递增子序列的个数(这是一个定理)

拦截所有导弹最少要配备的系统数:
使用贪心的算法进行证明

  1. 使用一个队列,过来一个导弹
         1.1 可以加入队列后面,那么就加入最小的后面(队列是 有序的,也就是二分查找大于等于 x 的最小数值,将他放在后面)
         1.2 不可以加入队列后面,新开一个
    你发现这个过程是完完全全和最上升子序列一样
    下面,我给出最长上升子序列的算法过程:

  2. f[i] 表示长度为 i 的上升子序列的最小末尾,对于每个加入的 val,我们进行一下的操作
          1.1 二分查找 f 数组中小于 val 的最大数值 f[j],然后将 val 放至 f[j + 1] 中,这一步可以看做是二分查找 f 数组中 大于等于 val 的最小数值 f[j] ,将 val 更新 f[j] 完全吻合!!

通过贪心,该算法得以证明
使用偏序集中的Dilworth 定理进行证明
首先,我们先介绍一下概念(参考百度百科)

偏序集合(英语:Partially ordered set,简写 poset)在数学中,特别是序理论中,是指配备了偏序关系的集合。这个关系形式化了排序、顺序或排列这个集合的元素的直觉概念。这种排序不必然需要是全部的,就是说不需要但也可以保证在这个集合内的所有对象的相互可比较性。(在数学用法中,全序是一种偏序)。偏序集合定义了偏序拓扑。

设R是非空集合A上的一个二元关系,若R满足: 自反性、反对称性、传递性,则称R为A上的偏序关系。

非严格偏序,自反偏序
   给定集合S,“≤”是S上的二元关系,若“≤”满足:
1 自反性:∀a∈S,有a≤a;
2 反对称性:∀a,b∈S,a≤b且b≤a,则a=b;
3 传递性:∀a,b,c∈S,a≤b且b≤c,则a≤c;
则称“≤”是S上的非严格偏序或自反偏序。

严格偏序,反自反偏序
   给定集合S,“<”是S上的二元关系,若“<”满足:
1 反自反性:∀a∈S,有a≮a;
2 非对称性:∀a,b∈S,a<b ⇒ b≮a;
3 传递性:∀a,b,c∈S,a<b且b<c,则a<c;
则称“<”是S上的严格偏序或反自反偏序。
   严格偏序与有向无环图(dag)有直接的对应关系。一个集合上的严格偏序的关系图就是一个有向无环图。其传递闭包是它自己。

极小元、链、反链定义
在X中,对于元素a,如果任意元素b,由b≤a得出b=a,则称a为极小元。
一个反链A是X的一个子集,它的任意两个元素都不能进行比较
一个C是X的一个子集,它的任意两个元素都可比

Dilworth 定理
对于一个偏序集,其最少链划分数等于其最长反链的长度。

Dilworth 定理是如何证明的呢?
该定理也可以理解为,对于一个偏序集,他的最长链的长度 等于 其最少反链划分数

  1. 首先,我们假设,他的最长链的长度为 xxx,最少反链划分数为 yyy
  2. 因为最长链中的元素并不满足反偏序的性质,因此,最少反链划分数量 yyy 应该大于等于 最长链的数量 xxx,即 y≥xy\geq xyx
  3. 设X1=X,A1是X1中的极小元的集合。从X1中删除A1得到X2。注意到对于X2中任意元素a2,必存在X1中的元素a1,使得a1<=a2。令A2是X2中极小元的集合,从X2中删除A2得到X3……最终,会有一个Xk非空而X(k+1)为空。于是A1,A2,…,Ak就是X的反链的划分,同时存在链a1<=a2<=…<=ak,其中ai在Ai内。由于xxx是最长链大小,因此xxx>=k。由于X被划分成了k个反链,因此x≥k≥yx\geq k\geq yxky
  4. 因此x=yx=yx=y,定理1得证。

参考链接

因此可以 O(NlogN)O(NlogN)O(NlogN)解决,代码对应如下

/*
问题一:一套系统,最多可以拦截多少导弹, 最长不上升子序列问题二:拦截所有导弹最少要配备的系统数:是用贪心的算法使用一个队列,过来一个导弹1. 可以加入队列后面,那么就加入最小的后面2. 不可以加入队列后面,新开一个你发现这个过程是完完全全和最长上升子序列算法过程一样
*/
#include <bits/stdc++.h>
using namespace std;const int INF = 0x3f3f3f3f;
const int N = 10010;
int a[N], f[N], len, n;int main() 
{   string s;getline(cin, s);stringstream ss(s);n = 0;while (ss >> a[++ n]);n --;/*for (int i = 1; i <= n; i ++ )printf("%d, ", a[i]);puts("");*/// 最长非递增子序列/*f[i] 应该是长度为 n 结尾的最大值每次应该是需要找 大于等于 x 的最小值*/memset(f, 0, sizeof f);f[0] = INF;len = 0;for (int i = 1; i <= n; i ++ ) {static int l, r, mid, val;l = 0, r = len, val = a[i];while (l < r) {mid = l + r + 1 >> 1;if (f[mid] >= val) {l = mid;} else {r = mid - 1;}}f[l + 1] = val;len = max(len, l + 1);}cout << len << endl;// 最长上升子序列memset(f, 0x3f, sizeof f);f[0] = 0;len = 0;for (int i = 1; i <= n; i ++ ) {static int l, r, mid, val;l = 0, r = len, val = a[i];while (l < r) {mid = l + r + 1 >> 1;if (f[mid] < val) {l = mid;} else {r = mid - 1;}}f[l + 1] = val;len = max(len, l + 1);}cout << len << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/474009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 900. RLE 迭代器(模拟/二分查找)

文章目录1. 题目2. 解题2.1 直接模拟2.2 二分查找1. 题目 编写一个遍历游程编码序列的迭代器。 迭代器由 RLEIterator(int[] A) 初始化&#xff0c;其中 A 是某个序列的游程编码。 更具体地&#xff0c;对于所有偶数 i&#xff0c;A[i] 告诉我们在序列中重复非负整数值 A[i …

html-css练习题(系统提示)

代码赏析&#xff1a;<!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible&qu…

机器学习算法--线性回归分析(单元和多元)

关键词 分类模型、回归模型 存在序的离散属性、不存在序的离散属性 有监督的机器学习 回归的分类&#xff08;输入变量数目&#xff0c;输入变量和输出变量的关系&#xff09; 已知数据集&#xff0c;未知参数 均方误差最小化&#xff0c;最小二乘法 一元线性回归 多元线…

LeetCode 740. 删除与获得点数(排序+动态规划)

文章目录1. 题目2. 解题1. 题目 给定一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除每个等于 nums[i] - 1 或 nums[i] 1 的元素。 开始你拥有…

html-css练习题 (注册表单)

代码赏析&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <meta http-equiv"X-UA-Co…

虚拟机安装 服务器 Ubuntu Server20.04.2

虚拟机安装 服务器 Ubuntu Server20.04.2 下载地址 VMware创建新的虚拟机 首选选择典型 稍后安装操作系统&#xff0c;并点击下一步 选择Linux Ubuntu64位 命名虚拟机 指定虚拟机的容量 点击确定之后开启虚拟机 选择自己下载的镜像文件 打开虚拟机进行配置 单击…

LeetCode 838. 推多米诺(模拟)

文章目录1. 题目2. 解题1. 题目 一行中有 N 张多米诺骨牌&#xff0c;我们将每张多米诺骨牌垂直竖立。 在开始时&#xff0c;我们同时把一些多米诺骨牌向左或向右推。 每过一秒&#xff0c;倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。 同样地&#xff0c;倒向右边…

html-css练习题(天天生鲜静态网页制作)文末有完整版代码地址链接

一、前言&#xff1a; 前端学习经典练手网页&#xff0c;重新整理网页版代码&#xff0c;如果你是初学者&#xff0c;请试着做一下这个网页 素材&#xff1a;文末完整版代码中。。。。。。 二、效果图&#xff1a; 三、主要需求&#xff1a; 1.login最外侧盒子设定高29 背景…

xshell连接Linux Server

由于server环境下无法使用vmtools&#xff0c;所以推荐使用shell工具进行链接使用。下载的内容源自果核&#xff0c;使用方法&#xff1a; xhell压缩包里面有crack目录&#xff0c;将nslicense.dll文件覆盖到软件目录 xhellplus&#xff0c;将nslicense.dll文件分别覆盖到软件X…

小案例:搭建简易版王者荣耀英雄购买商城网页版

目录 一、网页赏析&#xff1a; 二、代码分三部分&#xff1a; 1、html代码&#xff1a; 2、main.css赏析&#xff1a; 3、初始化reset.css代码&#xff1a; 三、主要素材下载地址&#xff1a; 一、网页赏析&#xff1a; 二、代码分三部分&#xff1a; 1.html 2.初始化…

2020云栖大会编程限时抢答赛 - 早中晚3场题解

文章目录1. 云栖大会限时抢答赛 - 早间场2. 云栖大会限时抢答赛 - 午间场3. 云栖大会限时抢答赛 - 晚间场1. 云栖大会限时抢答赛 - 早间场 题目链接 该场次题目在 LeetCode 上有原题&#xff0c;题解链接如下&#xff1a; LeetCode 862. 和至少为 K 的最短子数组&#xff08…

RSA公钥体系 与在 ssh中免密的登陆的应用

一、秘钥体系 第一部分查看书籍为 北京大学出版社出版的 丘维声老师的 数学思维方式与创新 在之前安全协议的讲解中&#xff0c;很多的协议都是用了秘钥的这一概念&#xff0c;相信很多同学对这不求甚解&#xff0c;下面我来系统的介绍秘钥体系&#xff0c;并且证明一下如今…

用Sass创建MetaFizzy效果

来源&#xff1a;GBin1.com Hugo发现了一个绚丽的css效果应用于web中并教会你如何聪明的重建和使用它。两天前&#xff0c;我看见笔者Hugo在css帮助下重写MetaFizzy的效果&#xff0c;Hugo帮助我们找到了一个用于理解Sass的JavaScript源代码。但我认为他的代码仍有可改之处&…

LeetCode 823. 带因子的二叉树(动态规划)

文章目录1. 题目2. 解题1. 题目 给出一个含有不重复整数元素的数组&#xff0c;每个整数均大于 1。 我们用这些整数来构建二叉树&#xff0c;每个整数可以使用任意次数。 其中&#xff1a;每个非叶结点的值应等于它的两个子结点的值的乘积。 满足条件的二叉树一共有多少个&…

动态规划问题之背包模型(18题)

背包问题是动态规划问题的一大类型&#xff0c;下面我们对这个进行总结。 以 Acwing y中总结的 几个类型&#xff0c;我写了几个题解 应用知识点 01背包、完全背包 空间压缩的写法多维费用的背包问题&#xff0c;以及状态的不同表示对复杂度的 影响完全背包问题的三种求解方法…

LeetCode LCP 22. 黑白方格画

文章目录1. 题目2. 解题1. 题目 小扣注意到秋日市集上有一个创作黑白方格画的摊位。 摊主给每个顾客提供一个固定在墙上的白色画板&#xff0c;画板不能转动。 画板上有 n * n 的网格。绘画规则为&#xff0c;小扣可以选择任意多行以及任意多列的格子涂成黑色&#xff0c;所选…

ubuntu server安装hadoop和spark,并设置集群

安装server请看本人的上一篇博客 Ubuntu Server 20.04.2 安装 先前准备工作 创建 hadoop用户 创建用户之后&#xff0c;输入一下指令重启 shutdown -r now登录 hadoop 用户 安装 ssh 和 vim 首先更新一下 apt 工具 sudo apt-get update然后安装vim sudo apt-get install…

LeetCode 第 35 场双周赛(216/2839,前7.61%)

文章目录1. 比赛结果2. 题目1. LeetCode 5503. 所有奇数长度子数组的和 easy2. LeetCode 5505. 所有排列中的最大和 medium3. LeetCode 5504. 使数组和能被 P 整除 medium4. LeetCode 5506. 奇怪的打印机 II hard1. 比赛结果 做出来3题。继续加油&#xff01; 全国排名&#…

Linux Ubuntu 安装 anaconda3和 Pycharm 社区版本

恰巧大数据实验安装 Anaconda & Pycharm&#xff0c;借此机会写篇博客分享一下 Anaconda 下载 Anaconda 清华镜像网站看一看 下载之后&#xff0c;放入 linux文件夹中 安装 Anaconda bash 下载的文件名 之后&#xff0c;按照提示进行输入信息(大多数都是 回车) 一定…

LeetCode 第 207 场周赛(245/4115,前5.95%)

文章目录1. 比赛结果2. 题目1. LeetCode 5519. 重新排列单词间的空格 easy2. LeetCode 5520. 拆分字符串使唯一子字符串的数目最大 medium3. LeetCode 5521. 矩阵的最大非负积 medium4. LeetCode 5522. 连通两组点的最小成本 hard1. 比赛结果 做出来3题&#xff0c;第四题试了…