hot 100 | 一文讲清动态规划
文章目录
- 动态规划(DP)简明理解与推导
- 一、核心概念
- 二、通用五步模板
- 三、例题一:爬楼梯(Climbing Stairs)
- 思路分析
- 推导过程
- Java代码
- 四、例题二:最大子数组和(Maximum Subarray)
- 思路分析
- 推导过程
- Java代码
- 五、例题三:0-1 背包问题(Knapsack)
- 思路分析
- 推导过程
- Java代码
- 六、一句话总结
动态规划(DP)简明理解与推导
一、核心概念
动态规划(Dynamic Programming, DP) 是一种算法思想:
把大问题拆成多个子问题,通过记忆化保存子问题的解,逐步推导出全局最优解。
一句话总结:
DP = 状态定义 + 状态转移 + 初始化 + 遍历顺序 + 答案输出
二、通用五步模板
步骤 | 内容 | 示例 |
---|---|---|
① | 明确状态 | dp[i] 表示第 i 个位置的最优值 |
② | 状态转移 | 根据前面状态推出当前 |
③ | 初始化 | 最小子问题直接给值 |
④ | 遍历顺序 | 通常从小到大 |
⑤ | 输出结果 | dp[n-1] 或 max(dp) |
三、例题一:爬楼梯(Climbing Stairs)
每次可以爬 1 或 2 个台阶,问爬上第 n 级有几种方法?
思路分析
要到达第 i
级,有两种可能:
- 从
i-1
级迈一步; - 从
i-2
级迈两步。
所以:
[
dp[i] = dp[i-1] + dp[i-2]
]
推导过程
i | 含义 | 推导公式 | 结果 |
---|---|---|---|
1 | 只有一种方法(1步) | dp[1] = 1 | 1 |
2 | 1+1 或 2步 | dp[2] = 2 | 2 |
3 | dp[2] + dp[1] | 2+1 | 3 |
4 | dp[3] + dp[2] | 3+2 | 5 |
5 | dp[4] + dp[3] | 5+3 | 8 |
Java代码
class Solution {public int climbStairs(int n) {if (n <= 2) return n;int[] dp = new int[n + 1];dp[1] = 1; dp[2] = 2;for (int i = 3; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2];}return dp[n];}
}
结果示例: n=5 → 输出 8。
时间复杂度:O(n),空间复杂度:O(n)。
四、例题二:最大子数组和(Maximum Subarray)
给定数组,找出和最大的连续子数组。
思路分析
定义 dp[i]
:以第 i 个元素结尾的最大连续子数组和。
状态转移:
[
dp[i] = \max(nums[i], dp[i-1] + nums[i])
]
解释:
- 如果前面的和
dp[i-1]
是负数,丢掉重开; - 否则接着加上当前数。
推导过程
以数组 [-2,1,-3,4,-1,2,1,-5,4]
为例:
i | nums[i] | 计算 | dp[i] | 当前最大 |
---|---|---|---|---|
0 | -2 | 初始 | -2 | -2 |
1 | 1 | max(1, -2+1) | 1 | 1 |
2 | -3 | max(-3, 1-3) | -2 | 1 |
3 | 4 | max(4, -2+4) | 4 | 4 |
4 | -1 | max(-1, 4-1) | 3 | 4 |
5 | 2 | max(2, 3+2) | 5 | 5 |
6 | 1 | max(1, 5+1) | 6 | 6 |
7 | -5 | max(-5, 6-5) | 1 | 6 |
8 | 4 | max(4, 1+4) | 5 | 6 |
最终最大值 = 6(来自子数组 [4, -1, 2, 1])
Java代码
class Solution {public int maxSubArray(int[] nums) {int currentSum = nums[0], maxSum = nums[0];for (int i = 1; i < nums.length; i++) {currentSum = Math.max(nums[i], currentSum + nums[i]);maxSum = Math.max(maxSum, currentSum);}return maxSum;}
}
时间复杂度:O(n)
空间复杂度:O(1)
五、例题三:0-1 背包问题(Knapsack)
有 n 个物品和容量为 C 的背包,每个物品有重量 w[i]、价值 v[i]。
每个物品最多选一次,求最大价值。
思路分析
dp[j]
= 背包容量为 j 时的最大价值。
对于第 i 个物品:
- 不放:价值不变;
- 放:容量减少 w[i],价值增加 v[i]。
转移公式:
[
dp[j] = \max(dp[j], dp[j - w[i]] + v[i])
]
推导过程
假设容量 C=5,物品:
物品 | 重量 | 价值 |
---|---|---|
A | 2 | 3 |
B | 3 | 4 |
C | 4 | 5 |
计算:
放 A → dp[2]=3
放 B → dp[3]=4
放 C → dp[4]=5
继续组合:
- 放 A+B:容量5 → 3+4=7 ✅
- 放 A+C:容量6>5 ❌
最大价值 = 7。
Java代码
class Solution {public int knapsack(int[] w, int[] v, int C) {int[] dp = new int[C + 1];for (int i = 0; i < w.length; i++) {for (int j = C; j >= w[i]; j--) {dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}}return dp[C];}
}
时间复杂度:O(n*C)
空间复杂度:O©
六、一句话总结
动态规划就是:
先确定状态,再找递推规律,最后一层层推到答案。