LeetCode题库之332、零钱兑换

154 阅读3分钟

零钱兑换问题简介:

零钱兑换问题是一个经典的动态规划问题,题目的核心是给定不同面额的硬币以及一个总金额,要求计算出组成总金额所需的最少硬币数量。

解决方案:

  1. 动态规划(Dynamic Programming)方法:

    • 使用一个数组 dp 存储每个金额所需的最少硬币数量。
    • 从金额为 0 开始到目标金额,逐步计算最少硬币数量。
    • 对于每个金额,遍历所有硬币面额,更新 dp 数组中的最小值。
    • 最终 dp[amount] 即为所需的最少硬币数量。
  2. 递归回溯方法:

    • 通过递归进行深度优先搜索,尝试每种硬币组合来达到目标金额。
    • 对于每个硬币面额,逐个尝试,直到找到最优解。
    • 递归方法的缺点是可能会遍历大量相同的组合,效率较低。

思考要点:

  • 处理边界条件:如目标金额为 0,或者没有硬币时的特殊情况。
  • 动态规划中的状态转移方程:确定状态转移方程是解决动态规划问题的关键。

解决方案之递归

通过递归,可以进行深度优先搜索,尝试不同的硬币组合,以确定达到目标金额所需的最少硬币数量。

  • 示例:
function coinChange(coins, amount) {
  // 递归函数,传入当前金额
  function helper(t) {
    // 递归结束条件
    if (t === 0) return 0;
    if (t < 0) return -1;

    let minCoins = Infinity;//使minCoins为最大值

    // 遍历每个硬币面额
    for (let coin of coins) {
      const res = helper(t - coin);
      if (res >= 0 && res < minCoins) {
        minCoins = res + 1;
      }
    }

    return minCoins !== Infinity ? minCoins : -1;
  }

  return helper(amount);
}
const coins = [1, 2, 5]; // 不同面额的硬币
const amount = 11; // 总金额
const result = coinChange(coins, amount);
console.log("最少硬币数量为:", result);
  • 以上代码中,使用coinChange 函数接受 coins(不同面额的硬币)和 amount(总金额)作为输入参数。
  • 设置一个helper 递归函数,它接受当前需要凑成的金额 t
  • helper 函数中,首先判断递归结束条件:当目标金额 t 等于 0 时,返回硬币数量为 0;当目标金额小于 0 时,返回 -1,表示无法解决。
  • 对于每个硬币面额,递归调用 helper(t - coin) 来求解剩余金额所需的最少硬币数量。
  • 在递归过程中,不断更新最小硬币数量 minCoins,直到找到最优解。
  • 返回最小硬币数量,如果无法凑成目标金额则返回 -1。

解决方案之动态规划

使用动态规划方法来解决零钱兑换问题,通过计算最小的硬币数量来凑成目标金额。动态规划的思想是从底向上逐步计算金额为 i 时的最少硬币数量,直至达到目标金额。

  • 示例
const coinChange = function (coins, amount) {
  const f = [];
  f[0] = 0;
  for (let i = 1; i <= amount; i++){
    f[i] = Infinity;//先设置为最大值
    for (let j = 0; j < coins.length; j++){
      if (i - coins[j] >= 0) {
        f[i] = Math.min(f[i], f[i - coins[j]] + 1);//取最小值 1表示当前硬币 
        //coins[j] 1个 f[i - coins[j]]表示剩余的钱数需要的最少硬币数
      }
    }
  }
  if (f[amount] === Infinity) {
    return -1;
  }
  return f[amount];
}
console.log(coinChange([1, 2, 5], 11));
  • 上述代码中,使用coinChange 函数接受 coins(不同面额的硬币)和 amount(总金额)作为输入参数。
  • 创建了一个f数组用于接收最少硬币数量
  • f[0](金额为 0 时)设为 0,因为凑成金额为 0 时,不需要硬币。
  • 使用两层循环进行动态规划的状态转移计算。外层循环遍历金额 i,内层循环遍历每个硬币的面额 coin
  • 在每次计算时,判断当前金额 i 是否大于等于当前硬币面额 coin。如果是,则更新 f[i]f[i - coin] + 1 和当前 f[i] 的最小值,表示凑齐金额 i 所需的最少硬币数量。
  • 返回 f[amount],即为凑成目标金额所需的最少硬币数量,如果无法凑成则返回 -1