【LeetCode】322. 零钱兑换
解题思路:动态规划(自底向上)
该问题属于经典的完全背包问题变种,核心目标是用最少数量的硬币凑成目标金额。动态规划是最高效的解法,其核心思想如下:
-
状态定义
dp[i]表示凑成金额i所需的最少硬币数量。若无法凑成,则值为-1。 -
状态转移方程
![]()
其中需满足 i≥coin 且 dp[i−coin]=−1。
-
边界条件
dp[0] = 0(金额为0时不需要硬币)- 其他值初始化为极大值(表示暂不可达)
-
算法选择
贪心算法在非规范硬币面值下可能失效(如coins=[25,21,10,1],amount=63,贪心得6枚,最优解为3枚),因此必须用动态规划保证最优解。
关键步骤
- 初始化
dp数组- 长度为
amount+1,dp[0]=0,其余初始化为amount+1(因为最多只需amount枚1元硬币)
- 长度为
- 遍历金额状态
对每个金额i(从1到amount),遍历所有硬币面值coin:- 若
coin <= i且dp[i-coin]可达(非初始值),则更新:dp[i] = min(dp[i], dp[i-coin] + 1)
- 若
- 处理结果
- 若
dp[amount]仍为初始值 → 返回-1 - 否则返回
dp[amount]
- 若
示例推导(
coins=[1,2,5],amount=11)
| 金额i| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|----------|---|---|---|---|---|---|---|---|---|---|---|----|----|
|dp[i]| 0 | 1 | 1 | 2 | 2 | 1 | 2 | 2 | 3 | 3 | 2 | 3 |
代码实现
示例测试
复杂度分析
| 维度 | 结果 | 说明 |
|---|---|---|
| 时间复杂度 | O(n·k) | n为金额,k为硬币种类数 |
| 空间复杂度 | O(n) | dp数组长度 = amount+1 |
| 适用数据规模 | n ≤ 10⁴ |
满足LeetCode约束 |
关键点总结
- 状态定义精准性:
dp[i]仅关注硬币数量而非组合方式,避免冗余计算。 - 初始化技巧:
用amount+1表示不可达状态,避免整数溢出(math.MaxInt可能过大)。 - 动态规划优势:
- 保证最优解:适用于任意硬币面值组合
- 高效性:单次遍历即可求解
- 与贪心对比:
算法 最优解保证 时间复杂度 适用场景 动态规划 ✅ O(n·k) 任意面值组合 贪心算法 ❌(可能失效) O(k) 规范面值(如欧元体系) - 边界陷阱:
- 金额为0时返回0(非-1)
- 硬币面值需为正整数(题目已约束)

浙公网安备 33010602011771号