給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
示例 1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3
解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins = [2], amount = 3
輸出: -1
說明:
你可以認爲每種硬幣的數量是無限的。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/coin-change
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
思路一:暴力法
思路二:貪心法
每次都用較大面額的硬幣去湊
可能會出錯比如給定 coins = [1, 6, 7], amount = 30, 使用貪心的話
res = 6, 47 + 21
而實際使用 5 個6元的硬幣即可
思路三:DP
硬幣問題跟爬樓梯問題有異曲同工之妙,可以想象成給定每次爬樓梯的步數和樓梯的階數,求最少多少步可以爬完樓梯,類似地,DP兩步走起~
-
定義狀態方程
DP[i]: 表示 i 階臺階,最少的步數
-
狀態轉移方程
DP[i] = min{DP[i-steps[j]]} + 1, j: 0 -> len(steps)-1
且有初始狀態 DP[0] = 0
碼代碼的時候注意是基於 DP[0], 從 DP[1] 開始形成整個 DP 數組,每次更新 DP[i] 時注意隱形條件:i >= step[j]
還有就是注意異常情況咯,見代碼
class Solution:
def coinChange(self, coins, amount) -> int:
if not coins or amount < 0: return -1
dp = [0] + [float("inf") for _ in range(amount)]
for i in range(len(dp)):
for coin in coins:
dp[i] = min(dp[i-coin] + 1, dp[i]) if i >= coin else dp[i]
return dp[-1] if dp[-1] != float("inf") else -1
思路三:完全揹包
仔細想想,該問題跟完全揹包問題也有些神似,可以理解爲 amount 爲揹包體積,coins 爲物品的體積,所求即剛好裝滿揹包的狀態下,所需最少的物品數量。