求递增子序列LIS的两种方法
文章目录
- 前言
- 一、普通动态规划(DP)求解LIS
- 1.DP思路
- 2.DP的状态定义与转移方程
- 3.DP的时间与空间复杂度
- 4.DP代码实现
- 5.DP的图文示例
- 二、贪心 + 二分查找求解LIS
- 1.思路分析
- 2.贪心 + 二分的时间与空间复杂度
- 三. 模板题讲解
- 1.洛谷B3637 最长上升子序列
- 1.dp写法
- 2.贪心+二分写法
- 2.洛谷P3902 递增
- 1.贪心+二分写法
- 四.练习题
- 1.洛谷P1091 [NOIP 2004 提高组] 合唱队形
- 1.思路分析
- 2.洛谷P1020 [NOIP 1999 提高组] 导弹拦截
- 1.思路分析
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
在开始讲解算法之前,我们先明确LIS的定义。
子序列:从一个序列中挑选一些元素(不要求连续),但必须保持原始相对顺序。例如,对于序列 [10, 9, 2, 5],[10, 2] 和 [9, 5] 都是子序列。
严格递增:子序列中的每个元素必须比前一个元素严格大。例如,[1, 3, 5] 是严格递增的,但 [1, 3, 3] 不是(因为有相等的情况)。
LIS问题:给定一个序列,找出其中最长的严格递增子序列的长度。
示例:
输入序列:[10, 9, 2, 5, 3, 7, 101, 18]
可能的递增子序列:
[10, 101](长度 2)
[2, 5, 7, 101](长度 4)
[2, 3, 7, 18](长度 4)
答案:最长递增子序列的长度为 4。
接下来,我们将详细介绍两种方法来解决这个问题
并且给出多道例题进行讲解
提示:以下是本篇文章正文内容,下面案例可供参考
一、普通动态规划(DP)求解LIS
1.DP思路
动态规划(DP)是一种通过将大问题分解为小问题来求解的方法。对于LIS,我们可以用DP逐步计算出每个位置的最优解,最终得到全局最优解。核心思想是:对于每个元素,考虑它能接在哪些之前的元素后面,形成更长的递增子序列
2.DP的状态定义与转移方程
状态定义:
定义 dp[i] 表示以 第 i 个元素 nums[i] 结尾的最长递增子序列的长度。
例如,dp[0] 表示以 nums[0] 结尾的LIS长度,dp[1] 表示以 nums[1] 结尾的LIS长度。
状态转移方程:
对于位置 i,我们需要检查它之前的所有位置 j(0 ≤ j < i):
如果 nums[j] < nums[i],说明 nums[i] 可以接在 nums[j] 后面,形成一个更长的递增子序列。此时,dp[i] 可以更新为 dp[j] + 1。
为了确保 dp[i] 是最大的,我们需要从所有满足条件的 j 中挑选 dp[j] 最大的值,然后加 1
dp[i] = max(dp[j] + 1) 对于所有 j < i 且 nums[j] < nums[i] 这里遍历所有可能的j情况取最大的那一种就行
如果没有满足条件的 j(即 nums[i] 比之前所有元素都小),则 dp[i] = 1,因为它自身就是一个长度为 1 的子序列。
初始化:
每个 dp[i] 初始值为 1,因为最短的递增子序列就是元素本身。
最终答案:
遍历整个 dp 数组,找到最大的 dp[i],这就是整个序列的LIS长度
3.DP的时间与空间复杂度
时间复杂度:O(n²)!!!
外层循环遍历每个位置 i(n 次),内层循环遍历 0 到 i-1(平均 n/2 次),总复杂度为 O(n²)。
空间复杂度:O(n)
只需一个长度为 n 的 dp 数组存储状态
4.DP代码实现
#include <iostream>
#include <vector>
#include <algorithm> using namespace std;int lengthOfLIS(vector<int>& nums) {int n = nums.size();if (n == 0) return 0; // 空序列返回 0vector<int> dp(n, 1); // 初始化 dp 数组,每个位置至少为 1int maxLen = 1;// 记录全局最大 LIS 长度for (int i = 0; i < n; i++) {以第i个元素为结尾 dp[i]for (int j = 0; j < i; j++) {//遍历结尾元素i前面的元素if (nums[j] < nums[i]) {dp[i] = max(dp[i], dp[j] + 1); // 更新 dp[i]}}maxLen = max(maxLen, dp[i]); // 更新全局最大值}return maxLen;
}int main() {vector<int> nums = {10, 9, 2, 5, 3, 7, 101, 18};cout << "最长递增子序列长度: " << lengthOfLIS(nums) << endl; // 输出 4return 0;
}
5.DP的图文示例

结果总结
最终 dp 数组:[1, 1, 1, 2, 2, 3, 4, 4]
LIS 长度:4
表格字段说明
索引 i:当前处理元素的下标。
nums[i]:序列中第 i 个元素的值。
dp[i]:以 nums[i] 结尾的最长递增子序列的长度。
计算过程:描述 dp[i] 是如何从前面的 dp[j](j < i)计算得到的。
可能的子序列示例:一个以 nums[i] 结尾的递增子序列,仅为示例,不一定是全局最优解
二、贪心 + 二分查找求解LIS
1.思路分析
普通动态规划(DP)求 LIS 的时间复杂度是 O(n²),因为它需要比较每个元素与之前所有元素的关系。而“贪心 + 二分查找”方法通过一种更高效的方式,将复杂度降到 O(n log n)。其核心在于:
1.目标:维护一个“最优”的递增子序列(不一定是最终的 LIS),确保每个长度的子序列末尾元素尽可能小。这样,后续元素就更容易接在这个子序列后面,从而最大化 LIS 的长度。
2.工具:使用一个数组 d,其中 tails[i] 表示长度为 i+1 的递增子序列的末尾元素的最小值。
3.操作规则:!!!!!!!!!!!!!!!!!!!!!!!!!!
如果当前元素大于 tails 的最后一个元素,直接将它追加到 tails 末尾,延长子序列。
如果当前元素小于等于 tails 的最后一个元素,用二分查找找到 tails 中第一个大于等于当前元素的位置,并替换它,优化某个长度的子序列末尾
。
重要说明:d数组本身不一定是最终的 LIS,但它的长度一定等于 LIS 的长度

这是算法竞赛入门到进阶这门书的原话解释,请大家仔细理解
关键点!!!
虽然 d 的长度是正确的,但它的元素只是用来维护这个长度的工具,不一定能直接对应原始序列中的一个实际递增子序列
但长度一定是最长的递增子序列的长度!!!!!!!!!!!!!
2.贪心 + 二分的时间与空间复杂度
时间复杂度:O(n log n)
遍历序列 n 次,每次二分查找复杂度为 O(log n)。
空间复杂度:O(n)
用于存储d 数组
三. 模板题讲解
1.洛谷B3637 最长上升子序列

题目不用分析,因为题意说的很清除了,现在我给出dp和贪心+二分的两种写法
1.dp写法
#include <iostream>
#include <algorithm>
using namespace std;
int n, arr[5005];
int dp[5005] ; // 以第i个数结尾的序列长度为dp[i];
int maxlen = 1;
int main()
{cin >> n;for (int i = 1; i <= n; i++){cin >> arr[i];dp[i] = 1;//初始化长度都为1 因为就是自己}for (int i = 1; i <= n; i++){for (int j = 1; j < i; j++)//遍历i之前的元素{if (arr[j] < arr[i])dp[i] = max(dp[i], dp[j] + 1);}}int maxa = 0;for (int i = 1; i <= n; i++){maxa = max(maxa, dp[i]);//找最大值}cout << maxa;return 0;
}
这样也能过,因为数据很小,n的范围我圈出来了,大家看上面的图
2.贪心+二分写法
#include <iostream>
using namespace std;
typedef long long ll;
const ll M = 1e5 + 5;
ll n, a[M], d[M], len;void slove()
{cin>>n;for (int i = 0;i<n;i++){cin >> a[i];}d[0]=a[0];len = 0;//长度为i+1 因为从0开始for (int i = 1;i<n;i++)//这里从1开始 因为0位置我们初始化了{if(a[i]>d[len])//直接放入即可{len++;//这个别忘记d[len] = a[i];}else if(a[i]<d[len]){//查找第一个大于或等于a[i]的元素ll pos = lower_bound(d, d + len + 1, a[i]) - d;d[pos] = a[i];//其实没找到也没啥 因为没找到会返回数组last的位置 所以这里不判断也没事 长度又没更新}}cout << len + 1; // 别忘记加1 因为从0开始
}signed main()
{//关流 加速输入输出ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);slove();return 0;
}
2.洛谷P3902 递增

大家仔细看n最大可以到1e5,那么就不能再用dp的双重循环了
如果有网友不信邪,可以自己试试哦哈哈哈
那么这里我直接给一份贪心+二分代码
1.贪心+二分写法
#include <iostream>
using namespace std;
typedef long long ll;
const ll M = 1e5 + 5;
ll n, a[M], d[M], len;void slove()
{cin>>n;for (int i = 0;i<n;i++){cin >> a[i];}d[0]=a[0];len = 0;//长度为i+1 因为从0开始for (int i = 1;i<n;i++)//这里从1开始 因为0位置我们初始化了{if(a[i]>d[len])//直接放入即可{len++;//这个别忘记d[len] = a[i];}else if(a[i]<d[len]){//查找第一个大于或等于a[i]的元素ll pos = lower_bound(d, d + len + 1, a[i]) - d;d[pos] = a[i];//其实没找到也没啥 因为没找到会返回数组last的位置 所以这里不判断也没事 长度又没更新}}cout << n-(len + 1); // 别忘记加1 因为从0开始
}signed main()
{//关流 加速输入输出ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);slove();return 0;
}
四.练习题
1.洛谷P1091 [NOIP 2004 提高组] 合唱队形

1.思路分析
题目意思我们可以翻译为从数组中找一个点p,然后从前到这个点找一个递增子序列,从后到这个点找一个递增子序列,使得两个子序列和最大就行

全部代码我放在GitHub上了,可以点击此处进入,记得挂梯子哦
2.洛谷P1020 [NOIP 1999 提高组] 导弹拦截

1.思路分析
第一个输出很常规,就是从后往前找一个最长递增子序列,因为题目说的是找递减的,因为后面的不能比前面的大,那我们反过来求就行
但第二个输出是一个关键点!!!!!!!
问题的本质
这个问题实际上是在问:如何用最少的严格递增子序列来覆盖整个序列。根据组合数学中的一个重要定理——Dilworth 定理(在偏序集上),我们可以得出以下结论
一个序列能被划分成的最少严格递增子序列的数量,等于该序列中最长的严格递减子序列!!!
换句话说,要解决这个问题,我们需要:
1.计算序列中最长的严格递减子序列的长度。
2.这个长度就是答案
那么我们一开始说了,这道题中我们从后往前求出来的就是递增子序列,所以显而易见,求递减子序列,我们只要从前往后就行
全部代码我放在GitHub上了,可以点击此处进入,记得挂梯子哦
总结
大家可以发现 稍微难一点的都会卡n的范围,故意设置到1e5,所以这个LIS的贪心+二分的方法很有必要学习
还有就是练习题第2道的求最少多少个 递增! 子序列可以覆盖全部数组数据的这个定理请牢记!
数组的最长严格 递减 !子序列的长度即为数量
相关文章:
求递增子序列LIS的两种方法
文章目录 前言一、普通动态规划(DP)求解LIS1.DP思路2.DP的状态定义与转移方程3.DP的时间与空间复杂度4.DP代码实现5.DP的图文示例 二、贪心 二分查找求解LIS1.思路分析2.贪心 二分的时间与空间复杂度 三. 模板题讲解1.洛谷B3637 最长上升子序列1.dp写法…...
【Linux篇】进程状态(僵尸进程,孤儿进程),优先级与调度机制
📌 个人主页: 孙同学_ 🔧 文章专栏:Liunx 💡 关注我,分享经验,助你少走弯路! 文章目录 1. 前文铺垫理解内核链表 2. 进程状态2.1 进程状态查看2.2 僵尸进程2.3 僵尸进程危害2.4 孤儿…...
SAP-ABAP:CONV(显示类型转换符)关键字详解
SAP ABAP CONV 关键字详解 CONV 是 ABAP 7.40 版本引入的显式类型转换操作符,用于将表达式的结果强制转换为指定的数据类型。它提供了一种清晰且类型安全的方式处理数据转换,避免隐式转换的潜在风险。以下是其核心特性和应用场景的全面解析:…...
AI应用加速落地丨MaxKB正在被政府、公共事业、教育和医疗行业用户广泛采纳
2025年2月至3月上旬,伴随着各个行业接入并使用DeepSeek,MaxKB开源知识库问答系统正在被越来越多的行业用户所采纳,是人工智能行业落地的强应用。目前,MaxKB在政府、公共事业、教育和医疗四大行业已经拥有了众多典型案例࿰…...
⚡️Jolt -- 通过JSON配置来处理复杂数据转换的工具
简介:一个能够通过JSON配置(特定的语法)来处理复杂数据转换的工具。 比如将API响应转换为内部系统所需的格式,或者处理来自不同来源的数据结构差异。例如,将嵌套的JSON结构扁平化,或者重命名字段࿰…...
Django系列教程(7)——路由配置URLConf
目录 URLconf是如何工作的? path和re_path方法 更多URL配置示例 URL的命名及reverse()方法 使用命名URL 硬编码URL - 不建议 URL指向基于类的视图(View) 通过URL传递额外的参数 小结 Django的项目文件夹和每个应用(app)目录下都有urls.py文件,它们构成了D…...
TDengine SQL 函数
单行函数 数学函数 ABSACOSASINATANCEILCOSDEGREESEXPFLOORGREATESTLEASTLNLOGMODPIPOWRADIANSRANDROUNDSIGNSINSQRTTANTRUNCATE 字符串函数 ASCIICHARCHAR_LENGTHCONCATCONCAT_WSLENGTHLOWERLTRIMPOSITIONREPEATREPLACERTRIMSUBSTRING/SUBSTRSUBSTRING_INDEXTRIMUPPER 转换函数…...
二维数组基础
在 C 语言中,二维数组是一种数据结构,它可以存储表格形式的数据,或是矩阵形式的数据。二维数组可以被看作是一个包含多个一维数组的数组,因此它有两个维度:行和列。 1. 二维数组的定义与声明 在 C 语言中,二维数组的定义形式如下: data_type array_name[rows][column…...
2024年第十五届蓝桥杯软件C/C++大学A组——五子棋对弈
蓝桥杯原题: 题目描述: “在五子棋的对弈中,友谊的小船说翻就翻? ” 不!对小蓝和小桥来说,五子棋不仅是棋盘上的较量,更是心与心之间的沟通。这两位挚友秉承着 “ 友谊第一,比赛第二…...
复试难度解析,西电先进材料与纳米科技学院学院考研录取情况
01、先进材料与纳米科技学院各个方向 02、24先进材料与纳米科技学院近三年复试分数线对比 PS:材料院24年院线学硕方向降低10分,专硕上涨15分;材料院在分数线相对于其他211、985院校对比来看,依然分数偏低,推荐大家关注…...
Deepseek Chatgpt Kimi 推荐的深度学习书单
朋友让推荐一些深度学习的书,让 Deepseek、Chatgpt、Kimi 分别生成了一份书单并做了对比,记录一下以备以后用到。 Chatgpt 推荐的深度学习书 1. chatgpt 推荐的书目截图 1.2 Chatgpt 推荐的深度学习书目文字版 如果你想学习 Deep Learning࿰…...
高频面试题(含笔试高频算法整理)基本总结回顾25
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言,…...
ClickHouse SQL优化:解锁高性能数据分析的关键
一、引言 1.1 ClickHouse的背景与优势 ClickHouse是一款高性能的列式数据库,专为在线分析处理(OLAP)场景设计。它以其卓越的读写性能、强大的数据压缩能力和灵活的SQL支持而闻名。ClickHouse能够轻松处理PB级数据,并在亚秒级内返回查询结果,这使其成为大数据分析领域的理…...
我与DeepSeek读《大型网站技术架构》(14)- 架构师领导艺术
文章目录 架构师领导艺术以人为本:激发团队潜能开放式协作:打破架构“所有权”壁垒妥协的艺术:聚焦核心目标成就他人:构建持续进化团队高效沟通:建立技术与人性的平衡 架构师领导艺术 本章聚焦架构师如何通过团队协作…...
mac安装mysql之后报错zsh: command not found: mysql !
在Mac上安装MySQL后,如果终端中找不到mysql命令,通常是 因为MySQL的命令行工具(如mysql客户端)没有被正确地添加到你的环境变量中。 检查 MySQL 是否已安装 ps -ef|grep mysql查看到路径在 /usr/local/mysql/bin 查看 .bash_pro…...
蓝桥杯备考:set容器用法(lower_bound)---营业额统计
如图所示,这道题的暴力解法就是枚举每天的营业额,让该营业额和前面的天的营业额依次相减取最小值这样的话我们的时间复杂度就是N平方,我们是很有可能超时的 所以我们选择用set容器的二分查找功能 我们每次遍历到一个数的时候,前…...
VSCode集成C语言开发环境
下载MinGW https://sourceforge.net/projects/mingw/ 点击download按钮下载exe文件到本地 点击exe文件安装 选择基础包和c编译版 vscode安装部分跳过 安装code runner和c/c插件 **(1) 创建 C 文件** 新建一个测试文件(例如 hello.c)…...
Python----数据可视化(pyecharts二:绘图一:条形图,直方图,折线图,散点图,箱图,饼图,热力图)
1、条形图 from pyecharts.charts import Bar from pyecharts.faker import Faker from pyecharts import options as opts # 绘制柱状图 bar (Bar() # 创建柱状图.add_yaxis("商家A", Faker.values(),colorFaker.rand_color()) # 添加数据.add_yaxis("商家B&…...
Training-free Neural Architecture Search for RNNs and Transformers(预览版本)
摘要 神经架构搜索 (NAS) 允许自动创建新的有效神经网络架构,为手动设计复杂架构的繁琐过程提供了替代方案。然而,传统的 NAS 算法速度慢,需要大量的计算能力。最近的研究调查了图像分类架构的无训练 NAS 指标,大大加快了搜索算…...
Linux机器之间排查网络连通问题
网络连通性排查步骤(基于五层模型) 以下按照网络五层架构,从底层到高层逐层排查,并分别列出Ubuntu和CentOS对应的命令。 1. 物理层 排查点:网线、网卡状态、物理连接。 命令(通用):…...
计算机考研C语言
C语言程序设计从入门到精通【2025完整版】考研复试 嵌入式 计算机二级 软考 专升本也适用_哔哩哔哩_bilibili 1、第一个C程序 helloC #include <stdio.h>int main(){printf("hehe");return 0;}每个C语言程序不管有多少行代码,都是从main函数开始执…...
【MySQL】(4) 表的操作
一、创建表 语法: 示例: 生成的数据目录下的文件: 二、查看表结构 三、修改表 语法: 另一种改表名语法:rename table old_name1 to new_name1, old_name2 to new_name2; 示例: 四、删除表 语法…...
Qt 中实现自定义控件子类化
一、子类化关键步骤 1、选择基类 根据需求选择合适的 Qt 原生控件作为基类(如 QWidget、QPushButton、QSpinBox 等),通过继承实现功能扩展。 2、重写关键方法 绘制逻辑:重写 paintEvent() 方法,使用 Q…...
[洛谷]P1123 取数游戏
最近准备蓝桥杯 一直在练搜索和图论hhh 题意 给定 n m n \times m nm的数字矩阵 可以取出若干数字 但是有限制 取出的两个数字不能在八联通方向上相邻 数据范围 1 ≤ N , M ≤ 6 , 1 ≤ T ≤ 20 1≤N,M≤6,1≤T≤20 1≤N,M≤6,1≤T≤20 思…...
DeepSeek 多模态大模型 Janus-Pro 本地部署教程
下载模型仓库 git clone https://github.com/deepseek-ai/Janus.git 国内下载仓库失败时,可以使用以下代理: git clone https://github.moeyy.xyz/https://github.com/deepseek-ai/Janus.git 准备 Conda 3.12 虚拟环境 conda create --name deepseek7B p…...
【prompt实战】知乎问题解答专家
本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权&am…...
6. MySQL 索引的数据结构(详细说明)
6. MySQL 索引的数据结构(详细说明) 文章目录 6. MySQL 索引的数据结构(详细说明)1. 为什么使用索引2. 索引及其优缺点2.1 索引概述 3. InnoDB中索引的推演3.1 索引之前的查找3.2 设计索引3.3 常见索引概念1. 聚簇索引2. 二级索引(辅助索引、非聚簇索引)…...
pytorch 50 大模型导出的onnx模型优化尝试
本博文基于Native-LLM-for-Android项目代码实现,具体做了以下操作: 1、尝试并实现将模型结构与权重零散的onnx模型进行合并,通过该操作实现了模型加载速度提升,大约提升了3倍 2、突破了onnxconverter_common 无法将llm模型导出为fp16的操作,基于该操作后将10g的权重降低到…...
LeetCode1871 跳跃游戏VII
LeetCode 跳跃游戏 IV:二进制字符串的跳跃问题 题目描述 给定一个下标从 0 开始的二进制字符串 s 和两个整数 minJump 和 maxJump。初始时,你位于下标 0 处(保证该位置为 0)。你需要判断是否能到达字符串的最后一个位置…...
RabbitMQ 从入门到精通
1 MQ架构设计原理 1.1 什么是消息中间件 消息中间件基于队列模型实现异步/同步传输数据 作用:可以实现支撑高并发、异步解耦、流量削峰、降低耦合度。 1.2 传统的http请求存在那些缺点 1.Http请求基于请求与响应的模型,在高并发的情况下,…...
