1. 程式人生 > >基於python的動態規劃經典問題(爬樓梯,取珠寶,最大子序列和,找零錢)

基於python的動態規劃經典問題(爬樓梯,取珠寶,最大子序列和,找零錢)

1,爬樓梯問題

一個人爬樓梯,每次只能爬1個或兩個臺階,假設有n個臺階,那麼這個人有多少種不同的爬樓梯方法

動態規劃的狀態轉移:第 i 個狀態的方案數和第 i-1, i-2時候的狀態有關,即:dp[i]=dp[i-1]+dp[i-2],dp表示狀態矩陣。

def climb_stairs(n):
    dp=[0]*n
    dp[0]=1
    dp[1]=2
    for i in range(2,n):
        dp[i]=dp[i-1]+dp[i-2]
    return dp[n-1]

2,取珠寶問題

一條直線上,有n 個房間,每個房間數量不等的財寶,一個盜賊希望從房屋中盜取財寶,由於房屋有警報器,同時從相鄰兩個房間盜取珠寶就會觸發警報,求在不觸發警報的情況下,最多可獲取多少財寶?

如有6個房間,每個房間珠寶數量為[5,2,6,3,1,7]

狀態轉移:

前1個房間可獲取珠寶的最大數量:5;

前2個房間可獲取珠寶的最大數量:5;

前3個房間可獲取珠寶的最大數量:11;

前4個房間可獲取珠寶的最大數量:11;

前5個房間可獲取珠寶的最大數量:12;

可以發現,前i個房間最大可獲取的數量和前i-1,i-2個房間可獲取的最大珠寶數量,以及第i個房間的珠寶數量有關:第i個房間珠寶有兩種方案,獲取(總最大數量等於第i個房間的珠寶數量+前i-2個房間的珠寶最大數量)和不獲取(總最大數量=前i-1個房間可獲取珠寶的最大數量)。

即:dp[i]=max(dp[i-2]+value[i],dp[i-1]),dp表示狀態矩陣。

def stealJewellery(value):
    n=len(value)
    dp=[0]*n
    dp[0]=5
    dp[1]=5
    for i in range(2,n):
        dp[i]=max(dp[i-2]+value[i],dp[i-1])
    return dp[n-1]

value=[5,2,6,3,1,7]
print(stealJewellery(value))  

3,最大子序列和問題

給定一個數組,求這個陣列的連續子陣列中,最大的那一段的和。

如陣列 arr= [-2,1,-3,4,-1,2,1,-5,4]

狀態轉移:

 如果當前陣列的值arr[i]加上第i-1個狀態的值大於陣列arr[i],第i個狀態的值就等於arr[i]+dp[i-1],

否則,dp[i]=arr[i]。其中dp[i]記錄的是以第i個數組結尾的最大欄位和

記錄當前最大的子序列和。即 dp[i]=max(dp[i-1]+arr[i],arr[i]),最大子序列和為max(dp)

def subsequenceSum(arr):
    n=len(arr)
    dp=[0]*n
    dp[0] = arr[0]
    for i in range(1,n):
        dp[i]=max(dp[i-1]+arr[i],arr[i])
    return max(dp)

4,找零錢問題

已知不同面值的鈔票,求如何用最少數量的鈔票組成某個金額,求可以使用的最少鈔票數量。如果任意數量的已知面值鈔票都無法組成該金額,則返回-1

假設coins=[1,2,5,7,10],金額:amount=14,dp[i]表示金額 i 的最優解。

金額14的最後一張面額可能由coins裡面的任意一張得到,即:

14=coins[0]+(14-coins[0]) \Rightarrow14=1+13 \Rightarrow dp[14]=1+dp[13]

14=coins[0]+(14-coins[1]) \Rightarrow14=2+12 \Rightarrow dp[14]=1+dp[12]

14=coins[0]+(14-coins[2]) \Rightarrow14=5+9 \Rightarrow dp[14]=1+dp[9]

14=coins[0]+(14-coins[3]) \Rightarrow14=7+7 \Rightarrow dp[14]=1+dp[7]

14=coins[0]+(14-coins[4]) \Rightarrow14=10+4 \Rightarrow dp[14]=1+dp[4]

具體最後一張選了哪個面額,就要看哪個面額情況下總拼湊數量最少。即:dp[i]=min(dp[i],dp[i-coins[j]]+1),當計算到金額14時候,小於14的金額的最小數量都已經求出並存儲在dp[i]裡面,此時只需要直接取出比較即可。最終函式返回dp[14]即為所求的最少數量。

求解程式碼一共有兩個for迴圈,一個是金額從0-amount的最少數量(dp[i]),另外一個是面額數量組合(coins[j])。dp[i]的初始值都設定為amount+1,當所有的coins都無法拼湊當前金額 i 的時候,dp[i] = amount+1 此時dp[i] 大於amount,所以函式返回-1。

def coinChange(coins,amount):
    dp=[amount+1]*(amount+1)
    dp[0]=0
    for i in range(1,amount+1):
        for j in range(len(coins)):
            if coins[j]<=i:
                dp[i]=min(dp[i],dp[i-coins[j]]+1)
    if dp[amount]>amount:
        return -1
    else:
        return dp[amount]

coins=[1,2,5,7,10]
print (coinChange(coins,14))