dp 数组含义定义一维数组 dp:
题目要求凑成 amount 的最少硬币数,所以最终答案就是:
如果某个金额无法被凑出来,就让它保持为一个很大的值,最后再判断是否需要返回 -1。
假设当前有一枚硬币 coin,现在要计算金额 j 的最少硬币数。
如果选择使用这枚硬币,那么在使用它之前,需要先凑出金额:
如果 j - coin 可以凑出来,那么再加上当前这 1 枚硬币,就可以凑出 j:
但是 dp[j] 可能之前已经通过其他硬币组合得到过更小值,所以要取最小值:
一句话总结:凑成金额 j 的最后一步,可以是先凑成 j - coin,再放入一枚面值为 coin 的硬币。
dp 数组如何初始化dp[0] = 0:
其他位置初始化为 Infinity:
原因是:一开始还不知道这些金额能不能被凑出来,用 Infinity 表示“暂时无法凑成”。
后续转移时,如果某个金额可以被凑出来,就会被更新成更小的硬币数量。
例如:
数组长度是 amount + 1,因为下标要从 0 一直到 amount,每个下标都表示一个具体金额。
这道题每种硬币可以使用无限次,所以是完全背包问题。
可以先遍历硬币,再正序遍历金额:
金额 j 必须正序遍历,因为 dp[j] 会依赖当前这一轮已经更新过的 dp[j - coin]。
这正好表示:同一种硬币可以被重复使用。
例如当前硬币是 2:
如果 dp[2] 已经在当前硬币 2 这一轮被更新过,那么 dp[4] 就可以继续使用第二枚 2。
dp 数组以 coins = [1, 2, 5]、amount = 11 为例。
初始化:
| 金额 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dp | 0 | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity | Infinity |
1硬币 1 可以凑出所有金额:
| 金额 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dp | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
例如:
表示凑成金额 3 最少需要 3 枚 1。
2加入硬币 2 后,很多金额可以用更少硬币凑出来:
| 金额 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dp | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 |
例如:
表示金额 4 可以用 2 + 2 凑出来,只需要 2 枚硬币。
5继续加入硬币 5:
| 金额 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dp | 0 | 1 | 1 | 2 | 2 | 1 | 2 | 2 | 3 | 3 | 2 | 3 |
例如:
此时 dp[6] = 2,表示金额 6 可以用 5 + 1 或 2 + 2 + 2 中更优的方式凑出来,再加一枚 5,金额 11 最少需要 3 枚硬币。
最终:
对应一种方案:
所以返回 3。
O(coins.length * amount),需要枚举每枚硬币和每个金额。O(amount),只需要一个一维 dp 数组。