LeetCode #322 Coin Change - 刷題之旅
1 題目描述
給定不同面額的硬幣coin
和一個總金額amount
,寫一個函數來計算可以製造出總金額的最少硬幣數量。如果無法製造出總金額,則返回-1
。
2 解法
其實dp的題目中有個共通點,都像是書包題目,為什麼是 dp 問題是因為可以拆分成小問題,而 amount 的小問題基本上就是 range(1, amount+1)
這些範圍如果都能夠找到最佳解ㄝ那自然amount
也能找到最佳解。所以我們腦子有個思路,就是我們可以先從1
開始。而最小值就是0
,因為0
元不需要硬幣。
小問題開始
1 | # 初始化 dp 碰到 0 的 amount 就是 0 |
接下來,我們假設: amount=11
且 coins=[3, 5]
,我們可以看到dp
的變化如下:
dp[0] = 0
dp[1] = inf
沒有對應的 coindp[2] = inf
沒有對應的 coindp[3] = 1
因為有 coin 3 可以組成dp[4] = inf
沒有對應的 coindp[5] = 1
因為有 coin 5 可以組成dp[6] = 2
當i>coin
時基本上有2種可能取最小dp[6-3] + dp[3] = 1+1 = 2
dp[6-5] + dp[5] = inf+1 = inf
dp[7] = 2
當i>coin
時基本上有2種可能取最小dp[7-3] + dp[3] = 1+1 = 2
dp[7-5] + dp[5] = inf+1 = inf
dp[8] = 2
當i>coin
時基本上有2種可能取最小dp[8-3] + dp[3] = 1+1 = 2
dp[8-5] + dp[5] = 1+1 = 2
有沒有發現上面有個規律
- 當
i > coin
時,我們可以看到dp[i]
的值是dp[i-coin] + dp[coin]
- 當
i < coin
時,dp[i]
的值是float('inf')
,因為無法組成 - 當
i == coin
時,dp[i]
的值是1
,因為只有一個硬幣就可以組成,但其實他也可以寫成dp[i] = dp[i-coin]+dp[coin]
因為會得到dp[0]+dp[coin]
然後所有的dp[coin]
都是1
,最後回傳值就是dp[amount]
,如果dp[amount]
是inf
就回傳-1
。
1 | dp = {0: 0} |
2.2 反過來
如果我們for loop
外層先從coin
開始,內層再for loop i
的好處是不用比較i
和coin
的大小,因為i
一定大於等於coin
。
可以讓程式碼更簡潔
1 | dp = [0] + [float('inf')] * amount |
時間複雜度:
- n 是 amount
- m 是 coins
空間複雜度: - n 是 amount
3 總結
所有碰到dp的題目,這種類似的背包問題都有個共通性…
那就是如果使用for loop
的時候,要從包背最小空間開始找到最佳解,也就是題目的for i in range(i, amount+1)
作為最外層開始。然後裡面的物品coin
就是內層的for coin in coins
,最後找到每個dp[i]
的最佳解。並且找到dp[i]
與上一個可行的最佳解。舉例來說:
- 目前i是
6
,coin是3
- 那就是
dp[6]
的最佳解是上一個最佳解dp[6-3]
是否要加上dp[3]
?
這感覺有點像是,我目前碰到3
的物品,要不要偷當前的3
來達成書包的最大化。
- 偷:
dp[6-3] + dp[3]
=至少空出3的書包空間
+3
- 不偷:
dp[6]
=目前塞滿的狀態
本部落格所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 Shannon's Blog 🐟 技術 | 生活 | 旅行! 如果你覺得我的文章有幫助,希望你可以到我的 github 給我一個 star ⭐️ Shannon Blog Repo
評論