Skip to main content

Coin Change (動態規劃)

題目需求

規定有一個要湊出來的金額 amount,然後有一袋硬幣 coins,硬幣面額有限但數量無限,請問有多少湊法?

舉例來說,就是如果有 1、2、5 三種面額的硬幣,要湊成 5 元可以有幾種湊法,答案是 4 種:

官方範例
Input: amount = 5, coins = [1,2,5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

題目解答

const change = function(amount, coins) {
const dp = new Array(amount + 1).fill(0)
dp[0] = 1

for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] += dp[i - coin]
}
}

return dp[amount]
}

解析

本題採用動態規劃解法,依照這篇文章的說法,很多時候動態規劃是解決重疊子問題的最佳解,也就是在解決一個複雜問題的過程中,該問題被分解成許多較小的子問題,且這些子問題之間不是完全獨立的,有很多子問題會被重複計算多次

解析本題解題流程:

  1. 建立了一個長度為 amount + 1 的陣列 dp,它是用來儲存湊成金額 i 的方法數量的陣列。因為還沒開始更新,所以先把值都設為 0。
const dp = new Array(amount + 1).fill(0)
  1. 建立了一個陣列後,把 dp[0] 設為 1,這裡的意思很簡單,就是湊成金額 0 的方法只有一個,就是都不拿任何硬幣。
dp[0] = 1
  1. 開始第一遍遍歷 (coin = 1),我們計算出使用硬幣面額 1 去湊出目標金額 i (1 ~ 5) 的方法有幾種。答案是都是一種,因為 1 = 12 = 1 + 13 = 1+ 1 + 1...以此類推,所以現在我們的陣列是:
dp = [1, 1, 1, 1, 1, 1]
  1. 接下來必須看一下內部的迴圈,所謂 dp[i] = dp[i] + dp[i-coin],它的含義是將當前金額 i 之前(考慮當前硬幣面額之前)的組合數(即 dp[i])加上新增加的這個硬幣面額 coin 後能夠組成的額外組合數(即 dp[i - coin])。:
for (let i = coin; i <= amount; i++) {
dp[i] += dp[i - coin]
}

但為何是 dp[i-coin]
這是因為如果你有一種方式可以組成金額 i - coin,那麼只需要再加上一個面額為 coin 的硬幣,就可以組成金額 i。因此,組成金額 i 的方式數量應該增加組成金額 i - coin 的方式數量,而這個 i-coin 的值都可以從前一次迭代中拿到。

參考資料

  1. 演算法筆記系列 — Dynamic programming 動態規劃
  2. 贾考博 LeetCode 518. Coin Change 2 - 这应该是hard
  3. LeetCode 518 Coins Change II解题分享 DP