LeetCode:322. 零钱兑换(python)
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:你可以认为每种硬币的数量是无限的。
思路:
-
递归(超时),递归遍历添加所有硬币的情况,每次添加
某硬币
,则递归遍历总金额-某硬币
的子问题。-
若
当前总金额'
为0
则表明当前硬币情况有解,返回0
。 -
若
当前总金额'
不为0
,则继续添加硬币
-
若
当前总金额'-硬币<0
表明不能添加该硬币(无解),尝试添加其他硬币。 -
若
当前总金额'-硬币>0
则递归遍历当前总金额-硬币
的子问题,若子问题无解,尝试添加其他硬币;若子问题有解,则每次添加硬币时记录,硬币数+1
,最终比较取最小值。
-
-
返回情况,若子问题有解,则返回子问题的
最少硬币数
,否则返回-1
。
-
-
记忆递归,以上递归所有硬币情况存在大量重复,添加记忆字典
{当前总金额:值}
进行剪枝,值为-1
表明,当前总金额的情况无解,否则值为当前总金额需要的最少硬币数。 -
动态规划
- 状态:金额
0
至金额amount
,dp
数组记录当前金额的最少硬币数。 - 选择:
当前金额
下是否添加该硬币
(遍历所有种类硬币情况),取决于所需硬币数的多少,若不添加则维持原硬币数;若添加则更新为dp[当前金额-该硬币]+1
。 - 初始值:
dp
数组维度为1×(amount+1)
,代表金额状态0~amount
;值为无穷大,方便比较最小值(所需硬币数最少)dp[0]=0
,表示金额为0
时,所需硬币数为0
。
- 返回值:判断
dp[amout]
- 若为无穷大,则表明该金额无法用所给的硬币种类求解;
- 否则返回
amount
金额下所需的最少硬币数dp[amount]
。
- 状态:金额
附代码1(Python3):递归(超时)
class Solution(object):
def coinChange(self, coins, amount):
if amount == 0: # amount 为 0 表示当前硬币情况有解,则返回 0
return 0
res = float('inf')
for coin in coins:
if amount-coin < 0: # 若添加该硬币无解则尝试其他硬币
continue
sub_count = self.coinChange(coins, amount-coin) # 递归遍历子问题
if sub_count == -1: # 若子问题无解则尝试其他硬币
continue
res = min(res, sub_count+1) # 子问题有解,硬币数加 1,取最小值
return res if res!=float('inf') else -1 # 子问题有解则返回 res 值,无解则返回 -1
test = Solution()
coins_li = [[1,2,5],
[2]
]
amount_li = [11, 3]
for coins, amount in zip(coins_li, amount_li):
print(test.coinChange(coins, amount))
3
-1
附代码2(Python3):记忆递归
class Solution(object):
def coinChange(self, coins, amount):
memo = {} # 记忆
return self.recurve(coins, amount, memo)
def recurve(self, coins, amount, memo):
if amount == 0:
return 0
if amount in memo:
return memo[amount]
res = float('inf')
for coin in coins:
if amount-coin < 0:
continue
sub_count = self.recurve(coins, amount-coin, memo)
if sub_count == -1:
continue
res = min(res, sub_count+1)
if res != float('inf'):
memo[amount] = res
return memo[amount]
else:
memo[amount] = -1
return memo[amount]
test = Solution()
coins_li = [[1,2,5],
[2]
]
amount_li = [11, 3]
for coins, amount in zip(coins_li, amount_li):
print(test.coinChange(coins, amount))
3
-1
附代码3(Python3):动态规划
class Solution(object):
def coinChange(self, coins, amount):
dp = [float('inf')]*(amount+1)
dp[0] = 0
for i in range(1, amount+1):
for coin in coins:
if coin <= i:
dp[i] = min(dp[i], dp[i-coin]+1)
if dp[amount] == float('inf'): # 该金额无解,则返回 -1
return -1
else:
return dp[amount] # 该金额有解,则返回最少硬币数
test = Solution()
coins_li = [[1,2,5],
[2]
]
amount_li = [11, 3]
for coins, amount in zip(coins_li, amount_li):
print(test.coinChange(coins, amount))
3
-1