leetcode 322 :零钱兑换(js)

384 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

题目

给你一个整数数组 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 <= 12 1 <= coins[i] <= 231 - 1 0 <= amount <= 104

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/co… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

  1. 同一个amount可能会有多个解,需要取最小的一个解

  2. 面额,即是粒度,如面额[2,5],最小粒度是2,那么面额值+小于最小粒度的值时不能被表示的,如3

  3. amount能用硬币表示的话,amount-面额也一定能被表示,如面额[2,5],如amount=9可以被5+2+2表示,那么9-2 = 7 9 - 5 = 4 也一定能用硬币表示

  4. 面额不是越大硬币越少,如amount=18 coins=[1,6,15] 取15 需要15+1+1+1 4个硬币,而取6只要6+6+6三个硬币

思路

分析下来,可以看出这个问题就有点像青蛙跳台阶了,amount就是台阶数,面额就是一次可跳的步数,现在要求的是青蛙用最少的跳跃次数,正好跳到第amount阶;

硬币个数其实就是跳的次数了,每跳一次可以加任一面额

那顺着这个思路,就可以用动态规划来做,下标amount对应当前amount最少的硬币兑换个数,dp[amount] = dp[amount-面额]+1

由以上1和4,可得需要对每一个面额时dp[amount-面额]的值比较得出最小值

代码实现

/** 
* @param {number[]} coins 
* @param {number} amount 
* @return {number} 
*/ 
var coinChange = function(coins, amount) { 
    let dp = new Array( amount + 1 ).fill( Infinity );  //动态规划,要求最小硬币个数,预设为无穷大
    dp[0] = 0; // 初始值
    for (let i = 1; i <= amount; i++) {  // 为每个能被兑换的amount填入最小硬币数
        for (let j=0; j<coins.length;j++) { 
            let coin = coins[j] // 当前面额
            if (i - coin >= 0)  // 大于面额值,才能被兑换
                dp[i] = Math.min(dp[i], dp[i - coin] + 1); // 需要比较减每个面额时,得到最小硬币个数
        } 
     } 
     return dp[amount] === Infinity ? -1 : dp[amount]; // 无解时,值为预设值,返回-1
 };

结果

image.png