当前位置: 首页 > news >正文

LIS(最长上升子序列)超全解析

一、LIS 是什么?

LIS(Longest Increasing Subsequence):给定序列 a,找出最长的一段子序列,满足严格单调递增(a[i]<a[j],i<j)。

  • 子序列:不要求连续,但顺序不变。
  • 常见变种:
    • 严格上升:a[i]<a[j]
    • 不下降:a[i]≤a[j]
    • 严格下降 / 不下降:把符号反过来即可。

二、问题模型(一句话)

输入:a[1..n]输出:最长上升子序列长度(有时也要输出方案)。

三、解法 1:朴素 DP(O(n^2),入门必会)

1. 定义状态

dp[i]:以 a[i]结尾的 LIS 长度。

2. 转移方程

dp[i]=max{dp[j]+1∣j<i, a[j]<a[i]}初始:dp[i]=1(每个元素自己是长度 1 的 LIS)。

3. 答案

max{dp[1..n]}。

4. 代码(C++)

int lis_dp(const vector<int>& a) { int n = a.size(), ans = 1; vector<int> dp(n, 1); for (int i = 1; i < n; ++i) for (int j = 0; j < i; ++j) if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1), ans = max(ans, dp[i]); return ans; }

5. 适用范围

n≤10^3 完全够用;数据再大就会超时。


四、解法 2:贪心 + 二分(O(nlogn),竞赛最优)

1. 核心思想(极关键)

维护一个数组 f:

  • f[k] 表示:长度为 k+1 的上升子序列的 “最小末尾”
  • 目的:让末尾尽可能小,为后面元素留出更大空间,从而得到更长序列。

遍历 a[i]:

  • 若 a[i]>f.back():直接接入,长度 + 1。
  • 否则:在 f 中找到第一个≥a[i]的位置,替换为 a[i](保持序列性质,不影响最长长度)。

最终 f 的长度就是 LIS 长度。

2. 为什么正确?

因为我们维护的是 “所有长度为 k 的上升子序列的最小结尾”,这样未来任何新元素能接上去的概率最大。

3. 代码(C++,严格上升)

int lis_optim(const vector<int>& a) { vector<int> f; for (int x : a) { auto it = lower_bound(f.begin(), f.end(), x); if (it == f.end()) f.push_back(x); else *it = x; } return f.size(); }

4. 若求 “不下降”(a[j]≤a[i])

lower_bound换成upper_bound

auto it = upper_bound(f.begin(), f.end(), x);

五、LIS 常见变种(导弹拦截 / 套路模板)

1)最长不上升子序列(LNIS)

要求 a[j]≥a[i]

  • 维护单调不上升数组 f
  • 二分:找第一个小于x 的位置(用greater
int lnis(const vector<int>& a) { vector<int> f; for (int x : a) { auto it = upper_bound(f.begin(), f.end(), x, greater<int>()); if (it == f.end()) f.push_back(x); else *it = x; } return f.size(); }

2)最长严格下降

同理,维护单调下降数组,用lower_bound配合greater


六、经典应用题(导弹拦截 P1020)

题目两问:

  1. 一套系统最多拦截多少:最长不上升子序列
  2. 最少需要多少套:根据 Dilworth 定理,等于最长严格上升子序列长度

所以代码直接套用上述模板,两问分别求即可。


七、避坑清单(笔试常错)

  1. 初始化不能为 0:dp [i] 初始 = 1;f 为空。
  2. 区分严格 / 不下降lower_bound/upper_bound别搞反。
  3. 比较器:降序变种必须用greater<int>()
  4. 答案是全局 max:O (n²) 答案是 max (dp);O (n log n) 答案是 f.size ()。

八、总结(背这句就够)

  • LIS = 最长上升子序列
  • 朴素 DP:O(n2),求以每个结尾的最长
  • 贪心 + 二分:O(nlogn),维护 “长度为 k 的最小末尾”
  • 变种只改比较器和二分条件,套路完全一样
http://www.jsqmd.com/news/589599/

相关文章:

  • OpenClaw浏览器自动化:Qwen3-32B镜像操控Chrome实战
  • 一文详解如何使用PHP进行正则表达式匹配
  • BCompare不止于代码:手把手教你用它做合同定稿、论文修订的文档对比神器
  • 学术海报自动生成:OpenClaw+Phi-3-vision科研工作流实践
  • 2026年沈阳正规的汽车贴膜实体店有哪些,汽车膜/玻璃膜/汽车贴膜/沈北贴膜/太阳膜/贴车衣,汽车贴膜专业店联系方式 - 品牌推荐师
  • 资源监控方案:OpenClaw+Qwen3-14B的GPU显存预警系统
  • OpenClaw+Phi-3-mini-128k-instruct个人知识库:自动整理收藏网页
  • OpenClaw+Qwen3.5-9B低成本运营:个人自媒体内容自动化生产
  • 从BERT到BERT4Rec:为什么双向建模在推荐系统中如此重要?
  • Wav2Vec 2.0:从海量无标签语音到精准识别的自监督学习之路
  • 2026年主播推荐手机补光灯厂家推荐与选型指南 - 品牌宣传支持者
  • MG811SpaceData:嵌入式端CO₂传感器四维建模与多气体解耦框架
  • 从零开始搭建FPGA开发环境:EP4CE22F17C8+WM8731音频处理实战指南
  • 从智能音箱到医疗设备:RC正弦波振荡器的10个意想不到的应用场景
  • 手把手教你用C语言实现Modbus RTU从站:从代码解析到实战调试(附完整工程)
  • OpenClaw知识管理:Qwen3.5-9B构建个人Wiki与智能问答
  • OpenClaw研究助手:千问3.5-9B驱动的文献综述自动化
  • OpenClaw植物养护仪:Qwen3-14b_int4_awq分析的传感器数据与照料建议
  • 【模电实战】—— 从纹波到稳定:整流滤波电路的工程设计与选型指南
  • Supabase注册与新增用户全解析:5个关键区别及适用场景指南
  • 数据库安全自查清单:你的Redis/MongoDB真的防住注入攻击了吗?
  • 别再死记硬背了!用这10个XSS-Labs关卡,手把手教你理解前端过滤与绕过逻辑
  • PyTorch与torchvision版本兼容性全解析:从安装到升级的避坑指南
  • 大疆照片的‘测绘模式’和‘畸变矫正’到底怎么用?一个案例讲清测绘项目中的元数据配置要点
  • OpenClaw+千问3.5-9B:自动化简历生成与优化
  • 避开ESP32音频开发的坑:新旧i2s驱动混用导致的CONFLICT错误排查与修复
  • Swagger-UI渲染异常排查指南:从版本校验到接口封装的解决方案
  • 学生-教师模型避坑指南:EfficientAD在MVTec数据集上的调参心得
  • OpenClaw+Phi-3-mini-128k-instruct个人博客系统:从构思到发布全自动
  • OpenClaw历史任务审计:追踪SecGPT-14B的所有安全操作记录