跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava算法

动态规划:最长递增子序列变体解析

综述由AI生成介绍动态规划中 LIS(最长递增子序列)问题的两种常见变体。通过贪心策略结合二分查找将时间复杂度优化至 O(n log n)。详细对比了严格递增场景(如俄罗斯套娃信封,需先排序)与非递减场景(如障碍赛跑)在二分查找条件上的区别,提供了完整的 Java 代码实现及核心逻辑解析。

橘子海发布于 2026/3/23更新于 2026/5/86.6K 浏览

文章配图

文章配图

1. 前置知识:什么是 LIS?

LIS = Longest Increasing Subsequence(最长递增子序列)

给定一个数组,求最长的、满足递增(或非递减)的子序列长度。

  • 朴素 DP:O(n^2),dp[i] = max(dp[j] + 1),j < i && nums[j] < nums[i]
  • 优化解法:贪心 + 二分 O(n log n)

贪心思想:维护一个数组 tails,tails[i] 表示长度为 i+1 的递增子序列的最小可能末尾值。越小,后面越容易接更长的序列。


2. 题目一:俄罗斯套娃信封(严格递增 LIS)

2.1 题目描述

给定若干个信封,每个信封以 [w, h] 表示宽度和高度。当且仅当信封 A 的宽度和高度 都严格大于 信封 B 时,B 可以套入 A。求最多能套多少层。

2.2 关键思路:二维 → 一维 LIS

我们希望:

  • 宽度已经有序,只需要判断高度
  • 高度满足严格递增 → 就是 LIS 长度
2.3 排序规则(非常重要)
Arrays.sort(envelopes, (a, b) -> {
    if (a[0] != b[0]) {
        return a[0] - b[0]; // 宽度升序
    } else {
        return b[1] - a[1]; // 宽度相同,高度降序
    }
});
2.4 为什么宽度相同要高度降序?

因为:宽度相同不能嵌套!

如果宽度相同、高度升序:[3,4], [3,5] 高度 4 < 5,LIS 会认为可以递增,结果错误。

降序排列:[3,5], [3,4] 5 > 4,不会被算进递增序列,保证正确性。

2.5 O(n log n) 贪心 + 二分代码
class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        if (envelopes == null || envelopes.length == 0) {
            return 0;
        }
        Arrays.sort(envelopes, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] - b[0];
            } else {
                return b[1] - a[1];
            }
        });
        int n = envelopes.length;
        int[] tails = new int[n];
        int len = 0;
        for (int[] e : envelopes) {
            int h = e[1];
            int idx = Arrays.binarySearch(tails, 0, len, h);
            if (idx < 0) {
                idx = -idx - 1;
            }
            tails[idx] = h;
            if (idx == len) {
                len++;
            }
        }
        return len;
    }
}
2.6 二分查找解释
  • 查找第一个 >= 当前高度 的位置
  • 替换它,让末尾尽可能小
  • 如果比所有都大,直接加入,长度 + 1

3. 题目二:最长障碍赛跑路线(非递减 LIS)

3.1 题目描述

给一个数组 obstacles,表示障碍高度。对每个位置 i,求:

  • 必须包含第 i 个障碍
  • 顺序不变
  • 高度 非递减(可以相等)的最长子序列长度。

3.2 关键点

这是 非递减 LIS,不是严格递增!允许:<=

3.3 与套娃信封的区别

  • 套娃信封:严格递增 → 找第一个 >= x
  • 障碍路线:非递减 → 找第一个 > x

只改一行二分条件!

3.4 完整代码

class Solution {
    public int[] longestObstacleCourseAtEachPosition(int[] obstacles) {
        List<Integer> list = new ArrayList<>();
        int n = obstacles.length;
        int[] ans = new int[n];
        for (int i = 0; i < n; i++) {
            int x = obstacles[i];
            int l = 0, r = list.size();
            while (l < r) {
                int mid = (l + r) / 2;
                if (list.get(mid) <= x) {
                    l = mid + 1;
                } else {
                    r = mid;
                }
            }
            if (l == list.size()) {
                list.add(x);
            } else {
                list.set(l, x);
            }
            ans[i] = l + 1;
        }
        return ans;
    }
}

3.5 为什么这么写二分?

我们要找:第一个大于 x 的位置 l

那么:

  • 0 ~ l-1 都是 ≤x
  • 所以以 x 结尾的最长长度 = l + 1

完美对应题目要求的非递减。


4. 两道题核心对比(一张表看懂)

题目序列类型二分查找目标
套娃信封严格递增第一个 >= x
障碍赛跑非递减第一个 > x

共同点:

  • 都是 LIS 模型
  • 都用贪心 + 二分优化到 O(n log n)
  • 都维护'最小末尾'数组

不同点:

  • 套娃信封需要先排序
  • 障碍路线直接求 LIS
  • 二分条件差一个等号

5. 文末总结

看完这两道题,你应该彻底掌握:

  1. LIS 贪心 + 二分是通用模板
  2. 严格递增 / 非递减 只在二分条件区分
  3. 套娃信封 = 排序 + 严格递增 LIS
  4. 障碍路线 = 非递减 LIS(每个位置求长度)
  5. O(n log n) 是大数据量下的唯一解法

目录

  1. 1. 前置知识:什么是 LIS?
  2. 2. 题目一:俄罗斯套娃信封(严格递增 LIS)
  3. 2.1 题目描述
  4. 2.2 关键思路:二维 → 一维 LIS
  5. 2.3 排序规则(非常重要)
  6. 2.4 为什么宽度相同要高度降序?
  7. 2.5 O(n log n) 贪心 + 二分代码
  8. 2.6 二分查找解释
  9. 3. 题目二:最长障碍赛跑路线(非递减 LIS)
  10. 3.1 题目描述
  11. 3.2 关键点
  12. 3.3 与套娃信封的区别
  13. 3.4 完整代码
  14. 3.5 为什么这么写二分?
  15. 4. 两道题核心对比(一张表看懂)
  16. 5. 文末总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 大模型 AI Agent 在企业应用中的 6 种基础类型与落地场景
  • 算法:双指针法求解有效三角形个数
  • pytest 入门指南:Python 测试框架核心概念与使用
  • Python 核心语法与实战入门
  • 大厂前端面试核心考点与高频原题汇总
  • Go 语言高性能 HTTP 库 fasthttp 使用指南
  • 基于 Termux 与 Ubuntu 在 Android 手机本地部署 OpenClaw 及 Llama 模型教程
  • ROS2 服务 Service Hello World 代码示例(C++ 版)
  • Python Django 鉴权方案全解析
  • 微软 BitNet.cpp 实现单 CPU 运行 100B 大模型无损推理与能耗优化
  • ComfyUI 提示词助手构建与自动化流程优化
  • Linux 命令行基础:从零开始实战指南
  • VS Code 集成 Git 操作指南:从环境配置到分支管理
  • 网络安全行业值得考的权威认证证书推荐
  • Flutter 三方库 webkit_inspection_protocol 在鸿蒙系统的适配指南
  • ChatGPT 提示工程编写指南与实战案例
  • Python 金融大数据分析快速入门与案例详解
  • 离线语音转录工具 Whispering 本地化方案解析
  • SLAM Toolbox 机器人定位与建图实战指南
  • 大模型时代的爬虫新范式:基于 Prompt 的数据提取实践

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online