LeetCode 「中等」322.零钱兑换

sankigan2025-2-27算法LeetCode

零钱兑换open in new window

题目描述

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例

输入:coins = [1, 2, 5], amount = 11
输出:3

输入:coins = [2], amount = 3
输出:-1

输入:coins = [1], amount = 0
输出:0

解答

动态规划

动态规划是一种通过分解问题存储中间结果来解决问题的方法。具体步骤:

  1. 初始化:
  • 创建一个数组 dp,长度为 amount + 1,初始值设为一个很大的数(比如 amount + 1),表示暂时无法凑成这些金额
  • dp[0] = 0,因为凑成金额 0 不需要任何硬币
  1. 状态转移:
  • 对于每个金额 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
  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];
};
更新于 2025/3/20 23:23:29