動態規劃系列之九找零錢

問題

給定不一樣面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算能夠湊成總金額所需的最少的硬幣個數。若是沒有任何一種硬幣組合能組成總金額,返回 -1。
你能夠認爲每種硬幣的數量是無限的。python

coins = [1,2,5]
amount = 11
結果:3,硬幣爲:5,5,1

解決過程

解題思路

動態規劃解題思路是:將大的問題拆解成小一點問題,小問題和大問題的解決思路是相似的
給定一個總金額11,有三種硬幣:1,2,5。
將問題的規模減小:湊11難湊,就湊10,若是10難湊就湊9,一直到湊1,湊0。算法

創建數學模型

添加數組dp,表示湊到某一個數值的最小硬幣數。如dp[1]就表明金額爲1的最少硬幣數,dp[10]就表明金額爲10的最少硬幣數。該dp數組長度爲12,從金額爲0到11,初始化爲:數組

[12,12,12,12,12,12,12,12,12,12,12,12]

之因此初始化爲12,是總金額+1,由於可能會存在湊不到這個數的狀況。當湊不到時,dp[-1]=12,湊獲得時,即便硬幣金額最小爲1,也只用11便可。app

狀態轉移方程

當要湊成的金額爲0時:函數

dp = [0,12,12,12,12,12,12,12,12,12,12,12]

金額爲1時
因爲硬幣有 一、二、5,因此,金額大於硬幣1的數額,因此一塊硬幣價值爲1便可code

dp = [0,1,12,12,12,12,12,12,12,12,12,12]

金額爲2時
金額爲2是,金額大於硬幣1,硬幣2,因此有兩種方案能夠湊齊。
一、某一個金額加上硬幣2,那麼就是金額0 + 硬幣2 dp[0] = 0,因此dp[2] = 1
二、某一個金額加上硬幣1,那麼就是金額1 + 硬幣1 dp[1] = 1,因此dp[2] = dp[1] + 1 = 2blog

選擇最小的,因此dp[2] = 1數學

dp = [0,1,1,12,12,12,12,12,12,12,12,12]

金額爲3時
金額大於硬幣1,硬幣2,因此有兩種方案
某一個金額加上硬幣2,就是 金額1 + 硬幣2 dp[3-2] + 1。dp[3-2],意思就是金額3減去硬幣2,獲得的金額1其最小的組成硬幣數。dp[3] = dp[3-1] + 1 = 2

某一個金額加上硬幣1,就是 金額2 + 硬幣1 dp[3-1] + 2。dp[3-1],意思就是 金額3 - 硬幣1,獲得的金額其最小組成的硬幣數。dp[3] = dp[3-2] + 1 = 2
因此,金額3時,dp[3] = 2class

dp = [0,1,1,2,12,12,12,12,12,12,12,12]

金額爲4時
金額大於硬幣1,硬幣2,因此有兩種方案
金額爲2 + 硬幣2,即 dp[4-2] + 1,dp[4] = 2
金額爲3 + 硬幣1,即 dp[4-1] + 1,dp[4] = 3
因此,金額爲4時,dp[4] = 2技巧

dp = [0,1,1,2,2,12,12,12,12,12,12,12]

金額爲5時
金額大於硬幣1,硬幣2,硬幣5,因此有三種方案
金額爲4 + 硬幣1,即 dp[5-1] + 1,dp[5] = 2 + 1 = 3
金額爲3 + 硬幣2,即 dp[5-2] + 1,dp[5] = 2 + 1 = 3
金額爲0 + 硬幣5,即 dp[5-5] + 1,dp[5] = 1
因此,dp[5] = 1

dp = [0,1,1,2,2,5,12,12,12,12,12,12]

最終按照這個規律,算出dp全部的數值。

代碼

示例 1:
輸入:coins = [1, 2, 5], amount = 11
輸出:3 
解釋:11 = 5 + 5 + 1
def coinChange(coins, amount):
    # 構建dp動態數組
    dp = [amount + 1] * (amount + 1)
    # 初始化
    dp[0] = 0
    
    for i in range(1, amount + 1):
        # 每個金額,全部能湊成的方案的硬幣數,最後取最小值
        temp = [dp[i]]
        for coin in coins:
            # 當金額大於某一個硬幣時才考慮,不然必定沒法用大額硬幣湊成小額
            if i >= coin:
                temp.append(dp[i-coin]+1)
        dp[i] = min(temp)
    print(dp)
    return -1 if dp[-1] == amount + 1 else dp[-1]

coins = [1, 2, 5]
amount = 11
res = coinChange(coins, amount)
print(res)

小結

動態規劃系列到這一篇就算完結了,我的感受動態規劃是算法中頗有難度,有技巧,有魅力的一種算法。爲了學明白這一種算法,我也不少次在深夜裏思考求解。當我以爲本身彷佛掌握了淺顯的規律以後,就把它記錄下來。這固然是我我的不太成熟的思想,動態規劃的深奧遠不是我能用9篇文章說明白的。可是,但願讀者能從這9篇文章學到一點個人經驗,摸到解決動態規劃問題的門檻。最後用我最喜歡的宮崎駿的動漫大集合做爲本篇的封面。

相關文章
相關標籤/搜索