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] = 0dp[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 = 2dp[6-5] + dp[5] = inf+1 = inf
dp[7] = 2當i>coin時基本上有2種可能取最小dp[7-3] + dp[3] = 1+1 = 2dp[7-5] + dp[5] = inf+1 = inf
dp[8] = 2當i>coin時基本上有2種可能取最小dp[8-3] + dp[3] = 1+1 = 2dp[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
評論