LeetCode | 518 Coin Change II

更新於 發佈於 閱讀時間約 6 分鐘

今天打開 leetcode,點進去它推送的 daily challenge,然後就成為了今天噩夢的開始...我要好好了解演算法了 QQ

題目是 518. Coin Challenge II,題目要求不難理解,就是規定有一個要湊出來的金額 amount,然後有一袋硬幣 coins,硬幣面額有限但數量無限,請問有多少湊法?

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

from: LeetCode

from: LeetCode

不難理解題目的意思吧。然後真的開始寫就爆炸了,我的 code 歷經了多輪測試層層挺進,最後死在了要用 1、2、5 去湊出 500 元來,給大家看一下有多慘哈...

raw-image

所以我忍不住了,直接點開 solutions 看大神怎麼解,然後歪唷,嗯,看某...還跑去問了 ChatGPT 才知道這用的是動態規劃的解法,而這題也是動態規劃的經典題。經典是經典,但為難了我這還沒出新手村的孩砸...。

Code 是這樣子的:

var 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];
}

一個陣列、兩個迴圈,簡簡單單,然後我跟 ChatGPT 拉扯了幾個小時、爬了幾篇動態規劃的文章,後來在中午吃飯時看了下面兩部影片才算是稍微知道這串 code 背後的邏輯思維是啥。我就稍微記錄一下我的理解哈,如果有理解錯拜託小力鞭 QQ



所以這串 code 到底搞了什麼事情,我嘗試理解拆解了一下。

1.) 建立了一個長度為 amount + 1 的陣列 "dp",它是用來儲存湊成金額 "i" 的方法數量的陣列。因為還沒開始更新,所以先把值都設為 0。

好了,為何要一個個把金額 i 列出來?題目不是只要求要湊到 amount 就好嗎?我後來理解這就是有關動態規劃的 "把大問題拆成很多小問題,然後由小問題的解組合成大問題的解" 的概念所在,很渾沌對吧?用下面例子說應該會比較清楚一點...

2.) 先建立了一個陣列後,把 dp[0] 設為 1,這裡的意思很簡單,就是湊成金額 0 的方法只有一個,就是都不拿任何硬幣。

raw-image

3.) 開始第一遍遍歷 (coin = 1),我們計算出使用硬幣面額 1 去湊出目標金額 i (1 ~ 5) 的方法有幾種。答案是都是一種,因為 "1 = 1"、"2 = 1 + 1"、"3 = 1+ 1 + 1"...以此類推,到這裡都還不難理解齁。

raw-image

4.) 第二遍遍歷 (coin = 2),噩夢的開始,天知道我花了多久在思考這裡。我們先再看一次內層的迴圈。

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

可以知道我們硬幣 2 要從目標金額 2 (i = 2) 開始湊了。但為什麼是dp[i] += dp[i - coin]

先再重申一次 dp[i] 是儲存湊成 i 金額方法數的地方,所以 dp[2] 就是儲存湊成 2 的方法數的地方,再一次強調,免得待會打結。

好,現在來看一下下面這張圖,我用紅色和藍色分別標記了兩個 1,是什麼意思?是這樣的:

  • 紅色的 1:dp[2] 在第一次遍歷時,也就是 coin = 1 時所儲存的方法數,也就是 "1 + 1" 這個組合。
  • 藍色的 1:dp[2] 在第二次遍歷時,也就是 coin = 2 時所要更新儲存進去的方法數。我們知道也就是只取 2 這一個方法,但我們應該想成 "0 + 2" 這個組合,而我們有沒有湊成 0 的方法數?答案是有的,在一開始就定義了,也就是 1。
raw-image

我們接著看 dp[3]:

  • 紅色的 1:在第一次遍歷時儲存了一個方法,也就是 "1 + 1 + 1"。
  • 藍色的 1:到了第二次遍歷時,我們多出了 "1 + 2" 這個組合,而湊成 1 的方法數到目前為止是 1。
dp[3]

dp[3]

再一次啊,來看 dp[4]:

  • 紅色的 1:在第一次遍歷時儲存了只用 coin = 1 的方法,也就是 "1 + 1 + 1 + 1"。
  • 藍色的 2:哇!變成 2 了,怎麼不是 1 了?其實按照剛剛的邏輯來看,我們用 coin = 2 要湊 i = 4 時,會得到 "2 + 2" 這個組合,那我們前面已經存過截至目前為止所儲存可以湊出 2 的方法數有 2 個,也就是 coin = 1 時儲存的 "1 + 1" 和迴圈才剛剛跑過去沒多久的 "0 + 2"
dp[4]

dp[4]

所以到這裡可以知道dp[i] += dp[i - coin]這裡面dp[i]代入的是前一次遍歷更新後湊成 i 的方法數,dp[i - coin]代表著使用了一次這個硬幣後,我們還需要湊成剩餘金額 "i - coin" 的湊法數量,而這個數量我們已經在前面的迴圈中儲存在 dp 中了。

到這裡我們可以看到,每次代入的參數其實都是前一次遍歷和迴圈帶來的結果,這樣一層一層堆砌成最後的解答,動態規劃的概念這就出現了。

最後我們會得到下面這張圖,也就是此時 dp = [1, 1, 2, 2, 3, 4]。而 4 就是我們最終的解答。

raw-image

e04!真的搞了我好久,我要好好去學演算法了...



參考資料:

  1. 贾考博 LeetCode 518. Coin Change 2 - 这应该是hard
  2. LeetCode 518 Coins Change II解题分享 DP


avatar-img
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Jeremy Ho的沙龍 的其他內容
JavaScript event loop / asynchronous.
JavaScript event loop / asynchronous.
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
題目敘述 Combination Sum II_Leetcode #40 給定一個整數陣列candidates 和 目標值target。 每個陣列元素只能選擇一次,請問有多少種組合方法,可以使得組合數總和 = target? 請把滿足組合數總和 = target的組合方法以陣列的形式返回答案
Thumbnail
題目敘述 Integer to English Words 給定一個整數num 請轉換成對應的的英文數字表達(One, Two, Three, ... 那種數字表達式)
Thumbnail
題目要求計算兩個二進位字串的相加,並以字串的形式輸出。 字串內容只包含'0'或'1'字元。 複雜度分析 時間複雜度為O(m+n),空間複雜度為O(m+n)。
Thumbnail
題目敘述 Combination Sum IV 給定一個輸入陣列nums,和目標值target,從nums裡面挑數字去湊出總和 = target,數字可以重複挑選。 請問有多少排列數可以湊出target? 註: 排列數的意思就是位置不同代表兩種不同的方法數。
Thumbnail
題目敘述 題目給定一棵二元樹,整棵樹剛好有n個節點 和 總共n枚金幣。 每個節點的值代表該節點初始擁有金幣的數量。 每回合可以給周圍的節點一枚金幣,請問最少需要幾回合才能讓所有節點恰好擁有一枚金幣? 原本的英文題目敘述
Thumbnail
題目敘述 題目會給定我們一個整數陣列nums,我們每回合可以挑選總和為K的兩個數字,形成一個K-Sum pair。 請問我們最多可以製造幾個K-Sum pair? 題目的原文敘述 測試範例 Example 1: Input: nums = [1,2,3,4], k = 5 Output
Thumbnail
題目敘述 題目會給定三個參數a, b, c。 請問透過bit flip a 或 b 的binary bits,讓 a OR b = c 最少需要幾次bit flip? 題目的原文敘述 測試範例 Example 1: Input: a = 2, b = 6, c = 5 Output:
Thumbnail
題目敘述 題目會給我們一個參數k 和 目標值n。 請問我們從1~9內挑k個相異的數字,使得他們的總和為n 的組合數有多少? 挑選時,每個數字必須相異,而且每個數字只能選一次。 題目的原文敘述 測試範例 Example 1: Input: k = 3, n = 7 Output: [
Thumbnail
這題也算是Leetcode 上經典的DP考題之一,也是很好的DP邏輯思考練習題。 題目敘述 題目會給我們一個nums陣列,分別代表每棟房屋的價值,也就是房屋內有的現金數量。 題目敘述給的情境是假想盜賊要偷東西,限制是相鄰的兩棟房屋不能一起偷,只能選擇其中一棟,否則就會觸發警報器。 請問怎麼選
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
題目敘述 Combination Sum II_Leetcode #40 給定一個整數陣列candidates 和 目標值target。 每個陣列元素只能選擇一次,請問有多少種組合方法,可以使得組合數總和 = target? 請把滿足組合數總和 = target的組合方法以陣列的形式返回答案
Thumbnail
題目敘述 Integer to English Words 給定一個整數num 請轉換成對應的的英文數字表達(One, Two, Three, ... 那種數字表達式)
Thumbnail
題目要求計算兩個二進位字串的相加,並以字串的形式輸出。 字串內容只包含'0'或'1'字元。 複雜度分析 時間複雜度為O(m+n),空間複雜度為O(m+n)。
Thumbnail
題目敘述 Combination Sum IV 給定一個輸入陣列nums,和目標值target,從nums裡面挑數字去湊出總和 = target,數字可以重複挑選。 請問有多少排列數可以湊出target? 註: 排列數的意思就是位置不同代表兩種不同的方法數。
Thumbnail
題目敘述 題目給定一棵二元樹,整棵樹剛好有n個節點 和 總共n枚金幣。 每個節點的值代表該節點初始擁有金幣的數量。 每回合可以給周圍的節點一枚金幣,請問最少需要幾回合才能讓所有節點恰好擁有一枚金幣? 原本的英文題目敘述
Thumbnail
題目敘述 題目會給定我們一個整數陣列nums,我們每回合可以挑選總和為K的兩個數字,形成一個K-Sum pair。 請問我們最多可以製造幾個K-Sum pair? 題目的原文敘述 測試範例 Example 1: Input: nums = [1,2,3,4], k = 5 Output
Thumbnail
題目敘述 題目會給定三個參數a, b, c。 請問透過bit flip a 或 b 的binary bits,讓 a OR b = c 最少需要幾次bit flip? 題目的原文敘述 測試範例 Example 1: Input: a = 2, b = 6, c = 5 Output:
Thumbnail
題目敘述 題目會給我們一個參數k 和 目標值n。 請問我們從1~9內挑k個相異的數字,使得他們的總和為n 的組合數有多少? 挑選時,每個數字必須相異,而且每個數字只能選一次。 題目的原文敘述 測試範例 Example 1: Input: k = 3, n = 7 Output: [
Thumbnail
這題也算是Leetcode 上經典的DP考題之一,也是很好的DP邏輯思考練習題。 題目敘述 題目會給我們一個nums陣列,分別代表每棟房屋的價值,也就是房屋內有的現金數量。 題目敘述給的情境是假想盜賊要偷東西,限制是相鄰的兩棟房屋不能一起偷,只能選擇其中一棟,否則就會觸發警報器。 請問怎麼選