LeetCode题解:518. 零钱兑换 II,动态规划,JavaScript,详细注释
chencl1986 opened this issue · comments
Lee Chen commented
原题链接:518. 零钱兑换 II
解题思路:
-
假设输入:
amount = 5, coins = [1, 2, 5]
,如果用长度为amount + 1
的dp
数组递推,需要满足如下特性:- 如果用长度为
amount + 1
的dp
数组递推。 - 每个索引代表了递推到的金额。
- 每个元素是硬币组合数。
0
位置是初始状态,设置为1
,表示都从1
种组合数开始递推。
- 如果用长度为
-
我们可以将其按照硬币种类拆分递推结果:
- 硬币
1
:[1,1,1,1,1,1]
- 硬币
2
:[1,0,1,0,1,0]
- 硬币
5
:[1,0,0,0,0,1]
- 硬币
1
、2
:[1,1,2,2,3,3]
- 硬币
1
、2
、5
:[1,1,2,2,3,4]
- 硬币
-
递推方法如下:
- 依次计算每个硬币面额
coin
的递推结果。 - 当前金额
i
,是从i - coin
添加了coin
而来。 - 当前新的硬币组合数
dp[i]
,等于上一种硬币递推到i
的组合数dp[i]
,加上i - coin
的组合数dp[i - coin]
而来。 - 状态转移方程为:
dp[i] = dp[i] + dp[i - coin]
。
- 依次计算每个硬币面额
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function (amount, coins) {
// 不断使用硬币组合,从0元一只组合到amount
// 数组保存的是每个金额可用的硬币组合数量
// 初始状态因为都没有递推,都为0
let dp = new Array(amount + 1).fill(0);
// 0位置设置为1,因为第一次递推都是从金额为coin开始
// 状态转移方程为dp[i] = dp[i] + dp[i - coin],这样dp[coin]的初始值就为dp[0]
// 由于第一次要投一个硬币,因此dp[0] = 1,能保证正常递推
dp[0] = 1;
// 分别计算每个硬币可以组合成amount的方法数量
for (const coin of coins) {
// 小于coin的金额是无法通过coin组合而成
// 一直递推到amount,就知道当前硬币组合成amount有多少种方法
for (let i = coin; i <= amount; i++) {
dp[i] =
// 保留之前硬币可以组合出当前金额的方法数量
dp[i] +
// 当前金额,必然是从i-coin,添加coin金额硬币之后组合而成
// 也就是当前金额是在i-coin的基础上变化而来,因此需要带上i-coin的组合数量
dp[i - coin];
}
}
// 最后组合成amount的方法数,就在amount位置
return dp[amount];
};