1. 程式人生 > >零錢兌換問題——python動態規劃解法

零錢兌換問題——python動態規劃解法

問題:

給定不同面額的硬幣(coins)和一個總金額(amount)。寫一個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合方式能組成總金額,返回-1

示例 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

示例 2:
coins = [2], amount = 3
return -1.

注意:

你可以認為每種硬幣的數量是無限的。(此題目摘自leetcode)

什麼是動態規劃?其思路是為了求解當前的問題的最優解,使用子問題的最優解,然後綜合處理,最終得到原問題的最優解。網上有很多介紹的文章,但是對於初學者(比如我)來說,看起來會有些懵。我個人的理解是,當總金額為amount時,所需的最少硬幣個數為dp[amount],那麼當amount = 11時,求出所有dp[1]、dp[2]、...、dp[11]的值。dp[1]到dp[10]就可以說是dp[11]的子問題,需要通過他們來求最終解。

以示例1為例,我們需要儘可能少的硬幣個數,所以從11的總金額中取出任意一枚硬幣,剩下的金額所需最少硬幣個數再加上1就是所需硬幣個數,即所需硬幣個數為:dp[10]+1、dp[9]+1、dp[6]+1,再從中取最小值,即可求解。以此類推,dp[10]=min(dp[9]+1,dp[8]+1,dp[5]+1)...所以可以看出,通過求出所有dp[1]、dp[2]、...、dp[10]的值,最終就能得到dp[11]的值。

以下是程式碼實現:

def coinChange(coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
coins.sort() #給硬幣從小到大排序
dp = {0:0} #生成字典dp,並且當總金額為0時,最少硬幣個數為0
for i in range(1,amount + 1):
dp[i] = amount + 1 #因為硬幣個數不可能大於amount,所以賦值amount + 1便於比較
for j in coins:
if j <= i:
dp[i]=min(dp[i],dp[i-j]+1)
#for i in range(1,amount + 1):
#print('dp[%d]:'%(i), dp[i])
if dp[amount] == amount + 1: #當最小硬幣個數為初始值時,代表不存在硬幣組合能構成此金額
return -1
else:

return dp[amount]

一開始不太理解,為什麼要求金額11的解需要算出1-10的所有解,這樣不是把問題弄得更復雜了嗎?後來醒悟是思路問題,這有些遞迴的思想在裡邊,就像蓋樓一樣,你不可能不用1-10層就只蓋個11層。之所以寫這篇文章,是因為想保留我現在作為初學者的視角,等以後學習更深入些再回過頭來說不定會有新的感悟,望大佬輕噴