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

动态规划中的 求“最长”、“最大收益”、“最多区间”、“最优策略” 双重 for + 状态转移

以最长递增子序列为例

🎯 首先明确目标

最长上升子序列(LIS)为例,假设输入是:

nums := []int{10, 9, 2, 5, 3, 7, 101, 18}

我们定义:

dp[i]:以 nums[i] 为结尾的最长上升子序列长度

目标:求 dp[i] 的最大值。


🔍 双重 for 的意义是什么?

for i := 1; i < n; i++ {for j := 0; j < i; j++ {if nums[j] < nums[i] {dp[i] = max(dp[i], dp[j] + 1)}}
}

这里我们来解释:

✅ 第一层循环:从左到右遍历每一个位置 i

我们要计算:以 nums[i] 结尾的“最长上升子序列”是多少?

✅ 第二层循环:查看所有在 i 之前的数 j

对于每一个前面的数 nums[j],判断:

它是否可以作为 nums[i] 的前一个元素?
也就是判断 nums[j] < nums[i]

如果是,就说明:

如果我以 j 为结尾的子序列有长度 dp[j]
那么我在它后面加上 nums[i],就可以构成一个更长的上升序列,长度是 dp[j] + 1

于是我们尝试更新 dp[i]

dp[i] = max(dp[i], dp[j] + 1)

🔁 用人话解释一遍:

“我想知道 nums[i] 能不能接在某个前面的上升序列后面,
如果能,那我就更新一下它作为结尾时,能组成多长的子序列。”


🧠 一个具体例子(带注释)

nums := []int{10, 9, 2, 5, 3, 7}初始化 dp := []int{1, 1, 1, 1, 1, 1}  // 每个位置起码是自己i = 3, nums[3] = 5j = 0, nums[0] = 10 -> 跳过j = 1, nums[1] = 9 -> 跳过j = 2, nums[2] = 2 -> 2 < 5 -> 更新 dp[3] = max(1, dp[2] + 1) = 2i = 4, nums[4] = 3j = 0 -> 10 > 3 -> 跳过j = 1 -> 9 > 3 -> 跳过j = 2 -> 2 < 3 -> dp[4] = max(1, dp[2]+1) = 2j = 3 -> 5 > 3 -> 跳过i = 5, nums[5] = 7j = 0 -> 10 > 7 -> 跳过j = 1 -> 9 > 7 -> 跳过j = 2 -> 2 < 7 -> dp[5] = max(1, 1+1) = 2j = 3 -> 5 < 7 -> dp[5] = max(2, 2+1) = 3j = 4 -> 3 < 7 -> dp[5] = max(3, 2+1) = 3

🧩 为什么不能只用一个循环?

因为我们求的是:

“在前面所有满足条件的数里,找一个最优的情况来更新当前的状态”。

只有看完所有的 j < i 才能找到最优的更新路径,所以必须有一个内层循环来“扫描过去”。


✅ 总结记忆方法:

  • i当前状态

  • j过去状态

  • if 条件成立,说明可以从过去 j 走到现在 i

  • dp[i] = max(dp[i], dp[j]+1) 就是取过去所有能转移过来的路径中最优的那一条

for i := 1; i < n; i++ {for j := 0; j < i; j++ {// 某种条件成立// dp[i] = max(dp[i], dp[j] + ...)}
}

这类“双重 for + 状态转移”的写法,在一类特定的动态规划问题中非常经典和高频。这类问题一般具有以下特征:


✅ 典型问题特征

  1. 子问题具有前后关系(i/j 结构)

    • 当前状态 i 要依赖过去某些状态 j < i

    • 类似“前缀分析”

  2. 具有单调性约束

    • nums[j] < nums[i]end[j] <= start[i] 等条件

  3. 求解最大/最小值

    • 求“最长”、“最大收益”、“最多区间”、“最优策略”


✅ 高频问题示例

问题类型描述dp含义转移条件
最长上升子序列 (LIS)找递增的最大长度dp[i]:以 i 结尾的最长长度nums[j] < nums[i]
最长不下降子序列可以等于也递增dp[i]nums[j] <= nums[i]
最长摆动子序列上下起伏交替up[i], down[i]比较大小后转移
最大不重叠区间数按 end 排序后贪心/DPdp[i]:前 i 个的最大区间数end[j] <= start[i]
最大信封嵌套数(俄罗斯套娃)求最多可嵌套信封数对宽升高做 LISw[j] < w[i] && h[j] < h[i]
打家劫舍变形不偷相邻的dp[i]:前 i 个最大金额dp[i] = max(dp[i-1], dp[i-2]+nums[i])
最大连续子数组积需要 max 和 minmax[i], min[i] 同时维护根据乘积正负转移

✅ 模板结构(可抽象成函数)

for i := 1; i < n; i++ {for j := 0; j < i; j++ {if 满足条件(j, i) {dp[i] = max(dp[i], dp[j] + 某个值)}}
}

✅ 小技巧:可以加上前向指针以恢复路径

prev := make([]int, n) // 记录转移路径
for i := range prev {prev[i] = -1
}
...
if dp[j] + 1 > dp[i] {dp[i] = dp[j] + 1prev[i] = j
}

如果你以后看到类似“最XXX的子序列”“最XXX的不重叠区间”,只要是“i依赖j”型的问题,几乎都可以优先尝试这个模板。

相关文章:

  • 问题六、SIMTOSIM部分遇到的问题及解决方法
  • IP隧道技术中数据包头部的变化分析:必然增加的封装机制
  • 自学嵌入式 day 23 - 数据结构 树状结构 哈希表
  • 语音合成之十五 语音合成(TTS)分句生成拼接时的响度一致性问题:现状、成因与对策
  • python开发环境管理和包管理
  • 布丁扫描高级会员版 v3.5.2.2| 安卓智能扫描 APP OCR文字识别小助手
  • 一、OpenCV的基本操作
  • 1537. 【中山市第十一届信息学邀请赛决赛】未命名 (noname)
  • 虚拟机Centos7:Cannot find a valid baseurl for repo: base/7/x86_64问题解决
  • 智能存储如何应对极端环境挑战?忆联独家解锁PCIe 5.0固态存储“抗辐射”黑科技,重新定义数据安全防护新高度
  • 2025 GEO优化战略图鉴:解码上海源易技术核心体系
  • Vue3和React中插件化设计思想
  • 解决PLSQL工具连接Oracle后无法使用ODBC导入器问题
  • [原理理解] 超分使用到的RAM模型和LLAVA模型
  • 60道Angular高频题整理(附答案背诵版)
  • Rancher 部署与使用指南
  • linux strace调式定位系统问题
  • DeepSeek 赋能智能电网:从技术革新到全场景应用实践
  • 李宏毅《机器学习2025》笔记 —— 更新中
  • java 进阶 1.0.3
  • 开办网站需要什么手续/电商网页
  • 深圳城乡和建设局网站首页/站长统计幸福宝下载
  • 域名解析网站登录/福州专业的seo软件
  • onedrive 做网站静态/开封网络推广公司
  • php是怎么设计网站的/杭州谷歌seo公司
  • 网站防劫持怎么做/企业宣传册