给定不同面额的硬币 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] <=
0 <= amount <=
示例代码1: 【dp数组的迭代解法】
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
dp = [0] + [10001] * amount
for coin in coins:
for i in range(coin, amount+1):
dp[i] = min(dp[i], dp[i-coin]+1)
return dp[-1] if dp[-1] != 10001 else -1
思路解析:
示例代码2:【dp数组的迭代解法】
class Solution(object):
def coinChange(self, coins, amount):
dp = [0] + [float('inf')] * amount
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[-1] if dp[-1] != float("inf") else -1
coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)
示例代码3: 【递归】
class Solution(object):
def coinChange(self, coins, amount):
def dp(n):
if n == 0:
return 0
if n < 0:
return -1
res = float("inf")
for coin in coins:
subproblem = dp(n - coin)
# ⼦问题⽆解,跳过
if subproblem == -1:
continue
res = min(res, subproblem + 1)
return res if res != float("inf") else -1
return dp(amount)
coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)
思路解析:
此时算法的时间复杂度较大,在力扣上提交是超时的。时间复杂度为:.
暴⼒解法就是遍历⼀棵
N
叉树:
示例代码4: 【带备忘录的递归】
class Solution(object):
def coinChange(self, coins, amount):
# 备忘录
memo = dict()
def dp(n):
# 查看备忘录,避免重复计算
if n in memo:
return memo[n]
if n == 0:
return 0
if n < 0:
return -1
res = float("inf")
for coin in coins:
subproblem = dp(n - coin)
# ⼦问题⽆解,跳过
if subproblem == -1:
continue
res = min(res, subproblem + 1)
# 记录备忘录
memo[n] = res if res != float("inf") else -1
return memo[n]
return dp(amount)
coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)
思路解析:
耗时的原因是重复计算, 那么可以造⼀个「备忘录」,每次算出某个⼦问题的答案后别急着返 回,先记到「备忘录」⾥再返回;每次遇到⼀个⼦问题先去「备忘录」⾥查 ⼀查,如果发现之前已经解决过这个问题了,直接把答案拿出来⽤,不要再 耗时去计算了。
很显然「备忘录」⼤⼤减⼩了⼦问题数⽬,完全消除了⼦问题的 冗余,所以⼦问题总数不会超过⾦额数 n,即⼦问题数⽬为 O(n)。处理⼀个 ⼦问题的时间不变,仍是 O(k),所以总的时间复杂度是 O(kn)。