DP動態規劃 深入淺出 以Coin change最精簡找零 為例

小松鼠-avatar-img
發佈於演算法題目解析 個房間
更新於 發佈於 閱讀時間約 7 分鐘
raw-image

這篇專欄有一個專屬的解題教學影片,搭配服用,效果更佳。





如同這個Dynamic programming 深入淺出系列的開始,在經過比較簡單的暖身題(費式數列)之後,來看進階一點的DP題目

一樣,再次強調DP的解題框架,鞏固知識點。


Dynamic programming本質是透過遞迴關係式,去解決一個大規模的問題,而這個大規模的問題又可以被分解為較小規模的子問題,而且子問題往往彼此重複

Dynamic programming的解題框架可分為三大步驟

1. 定義狀態 [我在哪裡]

2. 定義狀態轉移關係式(通則) [我從哪裡來] => [答案從哪裡推導而來]

3. 釐清初始狀態(也可以說是遞迴的終止條件) [第一步怎麼走,怎麼出發的]

以一個大家耳熟能詳的最精簡零錢找零Coin change來作為練習。

詳細的題目可在這裡看到

https://leetcode.com/problems/coin-change/

題目敘述

題目要求我們求出最精簡的零錢找零數目。範例輸入和輸出如下:

Example 1:

11元可以用最少3個零錢湊出,分別是$5兩枚和$1一枚

Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

3元無法用給定的零錢湊出(題目只給兩元銅板),返回-1

Input: coins = [2], amount = 3
Output: -1

Example 3:

0元不需要找零,返回0

Input: coins = [1], amount = 0
Output: 0



我們還是先走過一遍解題框架可分為三大步驟,藉此鞏固解題基礎。

(備註: 遞迴關係式和初始條件往往是要自己推導出來的,特別是在Medium/Hard或者競賽題)



1. 定義狀態 [我在哪裡]

這題沒有什麼疑問,

直接定義DP[n] =找出n元最精簡的找零零錢數目(用最少數目的銅板湊出)。

比較習慣數學符號思考的讀者,可以想成令f(n) = 找出n元最精簡的零錢數目。


2. 定義狀態轉移關係式(通則)
[我從哪裡來] => [答案從哪裡推導而來]

這裡題目也沒給,不過沒關係,我們可以試者推導看看。

用Example1來幫助我們思考

Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

我們最後要求的是DP[11], 也就是11元的最精簡找零零錢數目。

那11元可以怎麼去找零呢? 這裡題目給我們三種銅板,分別是1元、2元、5元銅板。

那麼我們就可以試著用1元、2元、5元銅板去找零

寫成數學式子就是

DP[11] = min( DP[11–1], DP[11–2], DP[11–5]) + 1

= min( DP[10], DP[9], DP[6]) + 1

後面那個+1是什麼呢? 就是代表這次找零我們用了一個銅板(可能是1元、2元、或5元,暫時還不知道,但是總是會從其中裡面選一個),所以+1

為什麼要取min最小值呢? 因為題目所求是最精簡的零錢找零數目(最少的銅板數目)

留意,其實到了這一步,我們已經把大規模的問題DP[n]分解成3個較小規模的子問題DP[n-1]、 DP[n-2]、 DP[n-5]。

並且,我們知道,在得到子問題的答案之後,可以推導出原本所求

DP[n] = min(DP[n-1], DP[n-2], DP[n-5]) + 1

寫成遞迴關係式就是DP[n] = min( DP[n-1], DP[n-2], DP[n-5] ) + 1

比較習慣數學符號思考的讀者,可以想成f(n) = min( f(n-1), f(n-2), f(n-5) ) + 1

接著問,那10元怎麼找開呢? 其實一樣,依此類推,我們可以試著用1元、2元、或5元試試看。

寫成數學式子,就是

DP[10] = min( DP[10–1], DP[10–2], DP[10–5]) + 1

= min( DP[9], DP[8], DP[5]) + 1

其他9元, 8元, 7元, ….等依此類推

到這邊,隱約看到一個找零問題的模板,可以寫成一個推廣後的通則,

針對n元,給定k種銅板Ci, i = 1, 2, … , k,最精簡的找零零錢數目如下

DP[n] = min( DP[n — C1 ], …, DP[n — Ck ]) + 1

for every valid coin $C1 to $Ck


3. 釐清初始狀態(終止條件)
[第一步怎麼走,怎麼出發的]

比較細心的讀者可能已經注意到,關鍵就是:

用每個銅板去縮小找零問題的規模,從n元找零問題一直化簡,降到0元的找零問題。

沒錯,正是如此。

但還是有終止條件必須仔細考慮。

降到0元怎麼辦? 0元還能再降下去嗎?不行

0元很明顯是我們這個題目的終止條件之一,當n=0時就不再往下遞迴。

0元的找零方法也很明顯,就是0代表不拿任何一枚銅板

降到負的值怎麼辦? 例如計算中可能出現n=4 去用5元銅板找零, 導致遞回到-1,這時該如何處理?

同上面的思考邏輯,合法的n值最小就是0,負的就應該停下來。

那…該回傳什麼值好呢?

考慮到這邊是”最小化”(最少數目的銅板去找零)的問題,我們可以用一個很大的正值或者無窮大,來代表: 這個金額找不開,無法找零

後續的程式碼,我們選擇用python語法中的無窮大float(‘inf’)來做示範。


到這裡,已經填滿解題框架的所有內容,接著我們把它轉成程式碼,

這裡以Python作為示範



程式碼 Top-down DP + 找零錢DP框架

class Solution:
  def coinChange(self, coins: List[int], amount: int) -> int:

    table = {}
    def dp( amount ):

      if amount < 0:
        # base case
        return float('inf')

      if amount == 0:
        # base case
        return 0

      if amount in table:
        # look-up table
        return table[amount]

      # general cases:
      table[amount] = min( dp( amount - coin ) + 1 for coin in coins )
      return table[amount]

    # ----------------------------------

    res = dp(amount)
    return -1 if res == float('inf') else res



複雜度分析

時間複雜度: O(cn)

真的n個不同目標金額,用c個銅板去計算最佳找零銅板數量。

空間複雜度: O(n)

DP table紀錄n個不同目標金額的最佳找零銅板數量。


回到DP特訓班目錄 和 學習路徑

avatar-img
90會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
今天要來討論 1 + "1" 。 如果當兩個操作數都是數字時,+ 會執行數字相加。例如,1 + 1 結果是 2。 那如果是"1"+"1",就變成字符串相加變成11。 那我們今天要講的是1 + "1",答案是11,為甚麼呢? 這是一個類型強制轉換,今天當 + 遇到不一樣的類型時,JavaScrip
Thumbnail
眾所周知,幣安的活動數量跟種類相當多,除了交易賽之外,還有一些很簡單的抽獎活動可以參加,像是「一美元遊戲」就是其中之一,它的參與步驟超簡單,只需要我們投入 1 USDT,就有機會將 1 USDT 換成價值 500 美元的加密貨幣!
Thumbnail
自小患有「數學讀寫障礙症」的叮噹,幾乎與數字絕緣。在學習過程中,舉凡所有跟數字和方向有關的概念,在辨別、混算和閱讀上,叮噹都比同齡學童表現遲緩,是不折不扣的數學蠢鈍兒。叮噹也曾判斷自己愚笨,無法成才,或多或少對成長造成不良的影響......
在求學階段,你已經對代數的計算熟到不能再熟,所以變數(variable)對你來說應該不至於太陌生,先來看看以下這個例子:   
描述了學習數學加法的過程與交易觀念的類比。一開始學習時可能會犯錯,需要不斷練習和檢討才能提高正確率,最終達到完美的正確率。同樣地,在交易中,重現獲利的關鍵在於重複和一致性,需要紀錄並觀察自己的交易模型,以找出不一致的地方,進而達到一致性。
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。
Thumbnail
在Python中,數值運算非常直觀,你可以使用標準的數學運算符號進行基本的數值運算。以下是一些基本的數值運算: 進行計算時,按照「先乘除後加減」的規則,並優先計算小括號刮起來的運算式。 print('答案:' ,(1+1)*2) #​答案: 4 復合型態的運算子 指定運算子 = 若是結合算術
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
今天要來討論 1 + "1" 。 如果當兩個操作數都是數字時,+ 會執行數字相加。例如,1 + 1 結果是 2。 那如果是"1"+"1",就變成字符串相加變成11。 那我們今天要講的是1 + "1",答案是11,為甚麼呢? 這是一個類型強制轉換,今天當 + 遇到不一樣的類型時,JavaScrip
Thumbnail
眾所周知,幣安的活動數量跟種類相當多,除了交易賽之外,還有一些很簡單的抽獎活動可以參加,像是「一美元遊戲」就是其中之一,它的參與步驟超簡單,只需要我們投入 1 USDT,就有機會將 1 USDT 換成價值 500 美元的加密貨幣!
Thumbnail
自小患有「數學讀寫障礙症」的叮噹,幾乎與數字絕緣。在學習過程中,舉凡所有跟數字和方向有關的概念,在辨別、混算和閱讀上,叮噹都比同齡學童表現遲緩,是不折不扣的數學蠢鈍兒。叮噹也曾判斷自己愚笨,無法成才,或多或少對成長造成不良的影響......
在求學階段,你已經對代數的計算熟到不能再熟,所以變數(variable)對你來說應該不至於太陌生,先來看看以下這個例子:   
描述了學習數學加法的過程與交易觀念的類比。一開始學習時可能會犯錯,需要不斷練習和檢討才能提高正確率,最終達到完美的正確率。同樣地,在交易中,重現獲利的關鍵在於重複和一致性,需要紀錄並觀察自己的交易模型,以找出不一致的地方,進而達到一致性。
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。
Thumbnail
在Python中,數值運算非常直觀,你可以使用標準的數學運算符號進行基本的數值運算。以下是一些基本的數值運算: 進行計算時,按照「先乘除後加減」的規則,並優先計算小括號刮起來的運算式。 print('答案:' ,(1+1)*2) #​答案: 4 復合型態的運算子 指定運算子 = 若是結合算術