我的 leetcode 刷题记录--零钱兑换

875 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

零钱兑换

题目链接:零钱兑换

题目描述:给你一个整数数组 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

解题思路

这题不能用贪心的思想来解,举个反例,coins=[1, 3, 5, 6, 7]amount=30,用贪心先用最大的面额 7,再用个 14 * 7 + 2 * 1 = 30,但是我们用 5 6 5 * 6 = 30 就能用最少的硬币兑换完成。

动态规划

dp[i] 表示兑换面额 i 所需要的最少硬币,因为硬币无限,所以可以自底向上计算 dp[i],对于 dp[0~i] 的每个状态,循环 coins 数组,寻找可以兑换的组合,用 i 面额减去当前硬币价值,dp[i-coin] 再加上一个硬币数就是 dp[i],最后取最小值就是答案,状态转移方程为 dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]])

题解

/**
 * @param {number[]} coins
 * @param {number} amount
 * @return {number}
 */
var coinChange = function(coins, amount) {
  // 初始化一个长度元素都为 amount + 1 的数组
  let dp = new Array(amount + 1).fill(amount + 1);  
  dp[0] = 0;

  for (let i = 0; i < dp.length; i++) {
    for (let j = 0; j < coins.length; j++) {
      if (i - coins[j] >= 0) {
        dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]]);
      }
    }
  }
  
  // 如果不是初始化的值说明兑换成功
  return (dp[amount] !== amount + 1) ? dp[amount] : -1;
};

时间复杂度:O(sn)O(sn)s 是兑换金额,n 是硬币数组长度,一共需要计算 s 个状态,每个状态需要遍历 n 个面额来转移状态

空间复杂度:O(s)O(s),也就是 dp 数组的长度

总结

此篇文章记录了笔者在 leetcode 刷题时的一些思考,对于零钱兑换使用动态规划的思想来进行解题,希望此文章能够为大家提供一些帮助,如有不足欢迎大家指正。谢谢!