给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
示例 3:
输入: coins = [1], amount = 0
输出: 0
提示:
1 <= coins.length <= 121 <= coins[i] <= 231 - 10 <= amount <= 104
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
//注意,memo数组保存总金额为i时的最少硬币数量,存的是硬币的数量哈
//memo[0]是coins硬币的情况下,amount = 0时需要的最少硬币数
//memo[1]是coins硬币的情况下,amount = 1,需要的最少硬币数
const memo = new Array(amount + 1).fill(-666);
// 备忘录初始化为一个不会被取到的特殊值,代表还未被计算
const dp = (coins, amount) => {
if (amount == 0) return 0;
if (amount < 0) return -1;
// 查备忘录,防止重复计算
if (memo[amount] != -666) return memo[amount];
let res = Infinity;
for (let coin of coins) {
// 计算子问题的结果,总金额 - coin币值
let subProblem = dp(coins, amount - coin);// coins硬币的情况下 总金额为amount-coin的最少硬币数
// 子问题无解则跳过,这个方案不合适
if (subProblem == -1) continue;
// 在子问题中选择最优解,然后加一
res = Math.min(res, subProblem + 1);
}
memo[amount] = (res == Infinity) ? -1 : res;
return memo[amount];
}
return dp(coins, amount);
};