一、题目描述:
leetcode322题:零钱兑换
给定不同面额的硬币 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:贪心算法
由题意可知,想要凑成总金额使用的硬币个数最少,我们肯定优先选择最大面额的硬币。因而可以采用贪心算法来解决。
- 硬币降序排列,从0到数组长度拿硬币,优先用大额硬币兑换
- amount / 硬币面值 取整 = 可用当前面值的硬币最多数量
- amount = 0,一次兑换结束,更新最小兑换数量
- 剪枝:记录兑换数量,后续兑换数量不能大于最小兑换数量
解法2:动态规划
该题还可以采用自底而上的动态规划来解决。
假设给出的不同面额的硬币是[1, 2, 5],目标是 120,问最少需要的硬币个数?
-
我们要分解子问题,分层级找最优子结构,看到这又要晕了哈,憋急~~ 下面马上举例。
-
这里我们使用「自顶向下」思想来考虑这个题目,然后用「自底向上」的方法来解题, 体验算法的冰火两重天。
-
dp[i]: 表示总金额为 i 的时候最优解法的硬币数
-
我们想一下:求总金额 120 有几种方法?下面这个思路关键了 !!! 一共有 3 种方式,因为我们有 3 种不同面值的硬币。
- 拿一枚面值为 1 的硬币 + 总金额为 119 的最优解法的硬币数量。这里我们只需要假设总金额 为 119 的最优解法的硬币数有人已经帮我们算好了,不需要纠结于此。即:dp[119] + 1
- 拿一枚面值为 2 的硬币 + 总金额为 118 的最优解法的硬币数。这里我们只需要假设总金额为 118 的最优解法的硬币数有人已经帮我们算好了,即:dp[118] + 1
- 拿一枚面值为 5 的硬币 + 总金额为 115 的最优解法的硬币数。这里我们只需要假设总金额为 115 的最优解法的硬币数有人已经帮我们算好了,即:dp[115] + 1
-
所以,总金额为 120 的最优解法就是上面这三种解法中最优的一种,也就是硬币数最少 的一种,我们下面试着用代码来表示一下:
-
dp[120] = Math.min(dp[120], dp[119] + 1, dp[118] + 1, dp[115] + 1);
-
推导出「dp方程」: dp[n] = Math.min(dp[n], dp[n - coin] + 1) 其中 coin 为硬币的面值,当然是 coins 数组中有几种不同面值的硬币,就是多少次了~ 遍历 coins 数组,分别去对比即可。
三、AC 代码:
贪心算法的代码
var coinChange = function(coins, amount, res = Infinity) {
coins.sort((a, b) => b - a)
var d = (amount, index, count) => {
if (amount === 0) return res = Math.min(res, count)
if (index === coins.length) return
for (var n = amount / coins[index] | 0; count + n < res && n >= 0; n--)
d(amount - n * coins[index], index + 1, count + n)
}
return d(amount, 0, 0), res === Infinity ? -1 : res
};
动态规划的代码
var coinChange = function(coins, amount, dp = [0].concat(Array(amount).fill(amount + 1))) {
for (var i = 1; i <= amount; i++)
for (coin of coins)
if (i >= coin) dp[i] = Math.min(dp[i], dp[i - coin] + 1)
return dp[amount] > amount ? -1 : dp[amount]
};
四、总结:
通过做零钱兑换这道题,首先温习了上周学习的动态规划,然后又学习了贪心算法的解题思想。贪心算法适合:在对问题求解时,总是做出当前最好的选择。零钱问题就是要优先选择最大面值的硬币匹配,因此可以采用贪心算法求解。
本文正在参与「掘金 3 月闯关活动」,点击查看活动详情