【leetcode】332. 零钱兑换(JavaScript)

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1
示例 2:

输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。

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

 看到题目的第一个想法是贪心,先取尽可能多的最大面额的硬币,再往后取,面额递减把总金额取满,取不满就返回-1 。但显然这是不对的,肯定会有不用取满最大面额的情况,于是便想到可以递归地去找,从最大数目的最大面额开始,递减地找,于是就有了如下代码:

var coinChange = function (coins, amount) { 
    const dfs = function(coins, sum, flag, count) {
        if (flag >= coins.length || sum < 0) {                            //(剪枝)
            return;
        }
        for (let i = Math.floor(sum/coins[flag]); i >= 0; --i) {
            let tempSum = sum - i * coins[flag];
            let tempCount = count + i;
            if (tempSum == 0) {                           
                ans = ans > tempCount ? tempCount : ans;
                break;                                            //剪枝,也可以直接return
            }
            if (tempCount+1 >= ans) {
                break;                                            //剪枝,也可以直接return
            }
            dfs(coins, tempSum, flag+1, tempCount);
        }
    }
    coins.sort((x, y) => x > y ? -1 : 1);
    const MAX = 2147483647;
    let ans = MAX;
    dfs(coins, amount, 0, 0);
    return ans == MAX ? -1 : ans;
}

我在注释中标了三处剪枝,其中第一处也可以算是正常的临界条件,但我看到leetcode上面有些人没加我就当成是一种剪枝了。

1. 临界条件这里的剪枝比较显而易见,当面额已经超过amount(sum)的时候,就没必要继续找了,注意此处不能取等,除非你单独加了另一条剪枝,当amount为0的时候直接输出0

2. 第二处剪枝其实跟第一处的意思差不多,如果等于0了就没必要继续往下找了

3. 第三处则在于,如果往后再找一步都不能更优的话,也没必要再找了,因为更小面额找到的肯定不会更少

上面这种解法跑出来的效果还是不错的(当然也有运气成分,不过基本是99%以上的):

另一方面也不难想到动态规划的解法:
f(amount)=min(f(amount-coins[k]) + 1))\left ( 0\leq k< coins.length \right )

于是我们可以自底向上递推出总币值为amount时所用硬币的最小值:

var coinChange = function (coins, amount) {
    const MAX = 2147483647;
    let ans = Array(amount+1).fill(MAX);
    ans[0] = 0;
    for (let i = 0; i < coins.length; ++i) {
        let k = 1;
        while (coins[i] * k <= amount) {
            if (k < ans[coins[i]*k]) ans[coins[i]*k] = k;
            k++;
        }
    }
    for (let i = 1; i < amount; ++i) {
        if (ans[i] != MAX) {
            let temp = ans[i];
            for (let j = 0; j < coins.length; ++j) {
                if (i + coins[j] <= amount) {
                    ans[i+coins[j]] = temp+1 > ans[i+coins[j]] ? ans[i+coins[j]] : temp+1;
                }
            }
        }
    }
    return ans[amount] == MAX ? -1 : ans[amount];
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值