力扣刷题322.零钱兑换

127 阅读1分钟

322.零钱兑换

给定不同面额的硬币 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

示例 4:

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

示例 5:

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

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104

解答:


动态规划:尝试分解子问题

-我们经常听到「最优子结构」「缩小问题规模」「自顶向下」「自底向上」等跟动态规划相关的词汇。
-但是想通都很不容易。
---------------开始解题-----------
1.假设给出的不同面额的硬币是[1,2,5],目标为120,问最少需要多少个硬币。
2.这里我们要分层级找最优子结构,下面举例子;
3.我们使用「自顶向下」来考虑这个问题,然后使用「自底向上」的方法解决问题。
-dp[i]:表示总金额为i时候的最优解法的硬币数;
-我们思考目标值120有多少种解法:
 1.拿面值为1的硬币 + 总额为119的最优解法的硬币数量;
   即:dp[119] + 1;
 2.拿面值为2的硬币 + 总额为118的最优解法的硬币数量;
   即:dp[118] + 1;
 3.拿面值为5的硬币 + 总额为115的最优解法的硬币数量;
 	 即:dp[115] + 1;
-所以目标120的解法就是这三种的最优解法的一种,也就是硬币数量最少的一种。
 即:dp[120] = Math.min(dp[119] + 1, dp[118] + 1, dp[115] + 1);
-我们可以推导出它的公式来
	dp[i] = Math.min(dp[i - coin] + 1, dp[i - coin] + 1, ...);
-要想得到dp[120],就要先得到dp[119],dp[118],dp[115];
 所以就应该从dp[0],dp[1],dp[2]往上推「自底向上」。

题解:

var coinChange = function(coins, amount) {
    //dp长度为amount+1,最后一个值为dp[amount]也就是最后要求的值
    let dp = new Array(amount+1).fill(Infinity);
    dp[0] = 0;
    for(let i=1; i<dp.length; i++) {
        for(let c of coins) {
            if(i - c >= 0) { //金额大于能使用的硬币面值
                //自底向上算出dp[1],dp[2],dp[3]...并保存起来
                //dp[11](目标值amount为11),coins为[1,2,5]
                //即dp[11] = Math.min(dp[10]+1(面额1), dp[9]+1(面额2), dp[6]+1(面额5));
                dp[i] = Math.min(dp[i], dp[i-c]+1);
            }
        }
    }
    //dp[amount]为初始值,表示不存在
    return dp[amount] === Infinity ? -1 : dp[amount];
};