【Leetcode】322.零钱兑换
322 零钱兑换
题目
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
方法(暴力递归)
如果想求 amount = 11 时的最少硬币数(原问题),若已经知道凑出 amount = 10 的最少硬币数(子问题),你只需要把子问题的答案加一(再选一枚面值为 1 的硬币)就是原问题的答案。因为硬币的数量是没有限制的,所以子问题之间没有相互制约,是互相独立的。
按如下步骤来考虑:
分析base case:当目标金额 amount 为 0 时算法返回 0,因为不需要任何硬币就已经凑出目标金额了。
分析可变参数,也就是原问题和子问题中会变化的变量。由于硬币数量无限,硬币的面额也是题目给定的,只有目标金额会不断地向 base case 靠近,所以唯一的可变参数就是目标金额amount。
确定决策,也就是导致可变参数产生变化的行为。目标金额为什么变化呢,因为你在选择硬币,你每选择一枚硬币,就相当于减少了目标金额。所以说所有硬币的面值,就是你的决策。
明确 dp 函数/数组的定义。如果是自顶向下的暴力递归情况,就会有一个递归的dp函数,一般来说函数的参数就是状态转移中会变化的量,也就是上面说到的可变参数;函数的返回值就是题目要求我们计算的量。
就本题来说,可变参数只有一个,即「目标金额」,题目要求我们计算凑出目标金额所需的最少硬币数量。所以我们可以这样定义 dp 函数:dp(n) 的定义:输入一个目标金额 n,返回凑出目标金额 n 的最少硬币数量。
代码
1 |
|
方法(动态规划)
以上暴力解法的代码转化为数学形式就是状态转移方程:
上述的暴力递归方法是自顶向下的,因此会产生很多重复计算。我们接下来把它改成自底向上的动态规划:
动态规划需要一个dp数组,dp数组的定义与上述的dp函数类似,其索引是要凑成的金额amount的值,索引对应的值为要凑成amount需要的最小硬币数。
代码
1 |
|