题目
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change-2
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
错误思路
/** * @param {number} amount * @param {number[]} coins * @return {number} */ var change = function(amount, coins) { let dp = new Array(amount + 1).fill(0); dp[0] = 1; for(let i = 1; i <= amount; i++){ for(let index=0;index<coins.length;index++){ if(i-coins[index]>=0){ dp[i]+=dp[i-coins[index]] } } } return dp[amount]; };
答案
dp三部曲,a.定义子问题,b. 定义状态数组,c. 定义状态转移方程。
用前k的硬币凑齐金额i,要分为两种情况考虑,
- 一种是没有用前k-1个硬币就凑齐了,
- 一种是前面已经凑到了i-k,现在就差第k个硬币了。
状态数组就是DP[k][i], 即前k个硬币凑齐金额i的组合数。
这里不再是一维数组,而是二维数组。第一个维度用于记录当前组合有没有用到硬币k,第二个维度记录现在凑的金额是多少?如果没有第一个维度信息,当我们凑到金额i的时候,我们不知道之前有没有用到硬币k。
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function(amount, coins) {
let dp = new Array(amount + 1).fill(0);
dp[0] = 1;
for(let i = 0; i < coins.length; i++){
for(let j = coins[i]; j <= amount; j++){
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
};
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function (amount, coins) {
let K = coins.length + 1;
let I = amount + 1;
/*js定义并初始化二维数组*/
let dp = new Array();
for (let k = 0; k < K; k++) {
dp[k] = new Array();
for (let i = 0; i < I; i++) {
dp[k][i] = 0;
}
}
//base case
for (let k = 0; k <= coins.length; k++) {
dp[k][0] = 1;
}
for (let k = 1; k <= coins.length; k++) {
for (let i = 1; i <= amount; i++) {
if (i >= coins[k - 1]) {
dp[k][i] = dp[k][i - coins[k - 1]] + dp[k - 1][i];
} else {
dp[k][i] = dp[k - 1][i];
}
}
}
return dp[coins.length][amount];
};