給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
說明:你可以認爲每種硬幣的數量是無限的。
鏈接:https://leetcode-cn.com/problems/coin-change
多種方法的思考:
方法1:遞歸
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
def count(n):
# 基本情況的判定
if n==0 : return 0
if n<1 : return -1
# 初始化
min_coins=float('INF')
for coin in coins:
# 子問題的分解
subproblem=count(n-coin)
if subproblem == -1:
continue
#
min_coins=min(min_coins,1+subproblem)
# 如果一直continue,min_coins 不變,就代表沒有可以分的可能
return min_coins if min_coins !=float('INF') else -1
return count(amount)
方法二: 加入記錄表的迭代
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
memo={}
def count(n):
# 查找子問題的值
if n in memo:
return memo[n]
if n==0 : return 0
if n<1 : return -1
# 初始化
min_coins=float('INF')
for coin in coins:
# 子問題分解
subproblem=count(n-coin)
if subproblem == -1:
continue
min_coins=min(min_coins,1+subproblem)
# 存儲子問題的解
memo[n]= min_coins if min_coins !=float('INF') else -1
return memo[n]
return count(amount)
方法三:用list代替dict中間表的迭代
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 初始化結果集
# 每一個下標對應對應amount,下標對應的最後元素值就是零錢的最小個數
res=[float('INF')]*(amount+1)
res[0]=0
for i in range(amount+1):
for coin in coins:
# 不可分就下一輪
if (i-coin) <0: continue
# 對每一個i進行計算
res[i]=min(res[i],1+res[i-coin])
if res[amount] == float('INF'):
return -1
else:
return res[amount]
方法四:利用整除減少循環次數
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n = len(coins)
coins.sort(reverse=True)
self.res = float("inf")
def dfs(index,target,count):
coin = coins[index]
if math.ceil(target/coin)+count>=self.res:
return
if target%coin==0:
self.res = count+target//coin
if index==n-1:return
print([range(target//coin,-1,-1)])
for j in range(target//coin,-1,-1):
dfs(index+1,target-j*coin,count+j)
dfs(0,amount,0)
return int(self.res) if self.res!=float("inf") else -1