# DP 定义的选择:以`nums[i]`结尾 vs 前`i`个元素

核心区别

定义方式 含义 适用场景
dp[i] = 以 nums[i] 结尾 强制包含第 i 个元素 子序列/子数组有连续性或依赖末尾元素的问题
dp[i] = 前 i 个元素的最优解 不强制包含第 i 个元素 可以自由选择是否使用第 i 个元素

关键判断依据

🔑 问法1:以nums[i]结尾

下一个状态依赖当前元素的值时使用

需要知道"上一个选的元素是什么"才能判断能否转移
         ↓
必须固定末尾元素 → 用"以nums[i]结尾"

特征:

  • 转移条件涉及元素大小比较(如递增、递减)
  • 最终答案需要遍历整个 dp 数组取最大值
  • 典型:dp[j] < dp[i] 这类比较

🔑 问法2:前i个元素

只关心用了多少个元素,不关心最后选的是谁时使用

第 i 个元素只有"选"或"不选"两种状态
         ↓
不需要知道上一个选的具体值 → 用"前i个"

特征:

  • 转移只需要索引位置,不需要元素具体值的比较
  • 最终答案通常直接是 dp[n]
  • 典型:背包问题、编辑距离

对比例题

例1:最长递增子序列 LIS → 用"结尾"

必须用"以nums[i]结尾"

原因:判断 nums[j] 能否接在 nums[i] 前面
      需要知道当前子序列的末尾值是多少
      
dp[i] = 以nums[i]结尾的最长递增子序列长度

转移:if (nums[j] < nums[i])  ← 依赖末尾元素的值
          dp[i] = max(dp[i], dp[j] + 1)

答案:max(dp[0], dp[1], ..., dp[n-1])  ← 需要遍历全部

例2:0-1背包 → 用"前i个"

必须用"前i个"

原因:物品只有选/不选,不需要知道上一个选了哪个物品
      只需要知道"用了前几个物品,容量还剩多少"

dp[i][w] = 前i个物品,容量为w时的最大价值

转移:dp[i][w] = max(dp[i-1][w],          ← 不选第i个
                     dp[i-1][w-weight[i]] + value[i])  ← 选第i个

答案:dp[n][W]  ← 直接取最后一个

例3:最长公共子序列 LCS → 用"前i个"

dp[i][j] = s1前i个字符 与 s2前j个字符 的LCS长度

转移:if s1[i] == s2[j]:
          dp[i][j] = dp[i-1][j-1] + 1
      else:
          dp[i][j] = max(dp[i-1][j], dp[i][j-1])

答案:dp[m][n]  ← 直接取最后一个

例4:最大子数组和 → 用"结尾"

dp[i] = 以nums[i]结尾的最大子数组和

原因:子数组必须连续!
      需要知道上一段是否值得"接上来"
      
转移:dp[i] = max(nums[i], dp[i-1] + nums[i])
              ↑ 重新开始      ↑ 接在前面

答案:max(dp[0..n-1])  ← 需要遍历全部

总结规律

问自己一个问题:
"转移时,我需要知道上一个选的元素的具体值吗?"

        ┌─── 需要(如:比大小、连续性)
        │         → 以 nums[i] 结尾
        │         → 答案 = max(dp[])
        │
        └─── 不需要(如:选/不选、匹配)
                  → 前 i 个元素
                  → 答案 = dp[n]
题目 定义 原因
最长递增子序列 nums[i]结尾 需比较元素大小
最大子数组和 nums[i]结尾 需判断是否连续接上
0-1背包 前i个物品 只关心选没选
最长公共子序列 前i个字符 只关心匹配了几个
编辑距离 前i个字符 只关心操作了几次
打家劫舍 前i个房子 只关心选没选
posted @ 2026-05-14 11:31  Tifa_Best  阅读(8)  评论(0)    收藏  举报