LeetCode 「中等」322.零钱兑换
题目描述
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例
输入:coins = [1, 2, 5], amount = 11
输出:3
输入:coins = [2], amount = 3
输出:-1
输入:coins = [1], amount = 0
输出:0
解答
动态规划
动态规划是一种通过分解问题并存储中间结果来解决问题的方法。具体步骤:
- 初始化:
- 创建一个数组
dp,长度为amount + 1,初始值设为一个很大的数(比如amount + 1),表示暂时无法凑成这些金额 dp[0] = 0,因为凑成金额 0 不需要任何硬币
- 状态转移:
- 对于每个金额
i(从 1 到amount),尝试使用每一种硬币coin - 如果
coin <= i,则更新dp[i]:这表示凑成金额dp[i] = Math.min(dp[i], dp[i - coin] + 1);i的最少硬币数是:- 当前已知的
dp[i],或者 - 使用一个面额为
coin的硬币后的结果dp[i - coin] + 1
- 当前已知的
- 最终结果:
- 如果
dp[amount]仍然是初始值(比如amount + 1),说明无法凑成金额amount,返回-1 - 否则,返回
dp[amount]
DETAILS
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
// 初始化 dp 数组,dp[i] 表示凑成金额 i 所需的最少硬币个数
// 初始化为 amount + 1,因为凑成 amount 所需的最大硬币数不会超过 amount(全部用 1 元硬币)
const dp = new Array(amount + 1).fill(amount + 1);
// 凑成金额 0 不需要任何硬币
dp[0] = 0;
// 遍历所有金额,从 1 到 amount
for (let i = 1; i <= amount; ++i) {
// 遍历所有硬币面额
for (let coin of coins) {
// 如果当前硬币面额小于等于当前金额
if (i >= coin) {
// 更新 dp[i],表示凑成金额 i 的最少硬币数
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
// 如果 dp[amount] 仍然是初始值 amount + 1,说明无法凑成该金额
return dp[amount] > amount ? -1 : dp[amount];
};
