這種思想具體的就是 從區域性最優解採用一定策略推導出全域性最優解,從子問題的答案中一步步推出整個問題的答案。
解決動態規劃問題通常採用四個步驟:
問題拆解(找到問題的子問題)
狀態定義(使當前狀態就是當前問題的解)
寫出遞推方程(之前相鄰子問題的答案推出當前狀態的答案)
**實現
通過幾個題目去理解動態規劃和這四個步驟。
問題描述:
[
[2][3,4]
[6,5,7]
[4,1,8,3]
]自頂向下的最小路徑和為 11 (即,2+3+5+1=11)
#說明如果你可以用o(n)的額外空間(n為三角形的總行數)來解決問題,那麼你的演算法會很加分.
#(這個是降低空寂複雜度的問題,實現演算法之後在考慮改善演算法,先不考慮他)
按照上面四步分析問題:
首先問題的拆解:
其實這個三角形可以看出來就是乙個二維陣列
這裡的問題是求最小路徑和,所有路徑是由乙個個元素(數字)組成的
所以問題就可以想象成:
每到達乙個元素的路徑是由當前元素加上當前元素的上乙個或兩個元素路徑得到的
進而把問題拆解成到達每乙個元素最小路徑的子問題
狀態定義:
狀態定義就是要和問題求解的答案緊密聯絡到一起
這個問題有兩個思路,乙個是從上到下找路徑,另乙個從下到上找路徑
首先我們看從上到下怎麼想:
這裡會有乙個問題,看下面例子:
[
[2][3,4]
[6,5,1]
[4,1,8,3]
]顯然最短路徑是 2 + 4 + 1 + 3 = 10
如果只考慮當前從2到3是最短,不選擇4,當到下一層時
根據問題要求(每一步只能移動到下一行中相鄰的節點上)就不能選擇1,問題答案就錯了。
還有乙個問題:
每一層元素的起始元素路徑只能選擇[i-1][j]即上一層的起始元素
最後元素選[i-1][j-1],即上一層最後乙個元素
但是中間元素就有[i-1][j],[i-1][j-1]兩種選擇
所以就不太好實現。我們用從下到上的思想。
從下到上的方式:
[
[2] [10] #我們可以看出[0][0]個元素就是我們的答案
[3,4] [9 8]
[6,5,1] [7 6 4] # 6可以選4和1,5可以選1和8 #記錄當前所有元素
[4,1,8,3] 從最後一層向上遞推 #到最後一層最短路徑
]我們定義的狀態就是 最後一行元素到當前所有元素最小路徑和。
遞推方程:
狀態定義好了,遞推方程就好寫了
第[i][j]個元素為當前狀態 = 三角形第[i][j]個元素加上([i+1][j]與[i+1][j+1]中小的)
dp[i][j] = ********[i][j]+min(dp[i+1][j],dp[i+1][j+1])
**實現:
在文末.
問題描述:
給定乙個整數陣列n,找到乙個具有最大和的連續子陣列
(子陣列最少包含乙個元素),返回其最大和。
例如:輸入:[1,-5,2,4,-1,3]
輸出:8
因為連續子陣列[2,4,-1,3]的和最大。
按照四步分析問題:
1.拆解問題
要找整個陣列中最大的子陣列和,就是去比較所有子陣列誰
的和最大.
子陣列可以看成乙個區間,都會有乙個起點和乙個終點定義.
由乙個起點到第 i 個點組成的子陣列 換句話說就是:
以第i個數結尾的子陣列,我們去求解所有以第i個數為結尾的
子陣列的最大和.
例如:[1,2,11,2,3]^|
第i個數是3,則以3結尾的最大和就是前第i-1為結尾,
此處是以2結尾的子陣列[1,2,-1,2]的最大和加上第i個數,也就是3.
[1,2,11,2,3]^|
第i個數是2,則以2結尾的最大和就是前第i-1為結尾,
此處是以1結尾的子陣列[1]的最大和加上第i個數,也就是2.
這樣我們就可以把問題從i 拆解成 i-1 即:
從第乙個起始元素開始推出第 i 個……直到整個陣列結束。
如果i-1結尾子陣列的最大和為負數,則i結尾字陣列最大和就是i.
2.狀態定義
dp[i] 就是以i為結尾的所有子陣列最大和.
3.遞推方程
dp[i] = max(dp[i-1],0) + array[i]
4 **實現
文末
問題描述:
一共有 n 階樓梯,每一次你可以爬乙個台階或兩個
你有多少種不同的方法可以爬到樓頂?
(其中,給定的 n 是乙個整數)
示例:輸入:2
輸出:2
因為兩階台階有兩種方式爬到樓頂
1. 爬一階再爬一階
2. 直接爬兩階
按照四步分析問題:
1.拆解問題
一共n階台階,有兩種方式爬,所以我們到達第n階的時候
不是從第n-2爬上來,就是從第n-1階爬上來,所以我們把
問題看成到達第 n-2 階時所有方式加上到達第n-1階時
所有方式就是到達第n階的所有方式.
2.狀態定義
第 i 個狀態dp[i]就是到達第 i 階樓梯時存在的所有爬樓
梯方法種類數量.
3.遞推方程
dp[i] = dp[i-1] + dp[i-2]
4.**實現
文末
'''
三角形最小路徑和(動態規劃問題)
'''class solution(object):
def minimumtotal(self, ********):###原始演算法
if not ********:
return 0
n=len(********)
dp=[[none]*n]*n
array = ********[-1]
for i in range(0,n):
dp[n-1][i] = array[i]
for i in range(n-2,-1,-1):
for j in range(0,len(********[i])):
dp[i][j] = min(dp[i+1][j],dp[i+1][j+1])+********[i][j]
return dp[0][0]
def minimumtotal_test(self, ********):#####改進演算法
if not ********:
return 0
h = len(********) # 金字塔高度
lay = ********[-1] # 取到金子塔最下面一層:是乙個列表
i = h - 2 # 取到金字塔層數的倒數第二層: 是乙個數字
while i >= 0:
j = 0 # j:遍歷金字塔每一層中元素指標
while j <= i: # 本來行數和每層的元素數量是一致的,但索引從0開始所以減1,底下j有j+1所以再次減1,故直接是j<=i
lay[j] = min(lay[j], lay[j + 1]) + ********[i][j] # 對上一層的乙個值找其正下方和左下方的最小值,賦到其上
j += 1
i -= 1
return lay[0] # i為0時取到的是最上面一層,最上面一層只有乙個值
if __name__ == '__main__':
s=solution()
print(s.minimumtotal_test([[2],[2,3]]))
'''輸入乙個整形陣列,可能有正有負,求陣列中連續子陣列的最大和
要求時間複雜度為o(n)
'''class solution:
def multiply(self , a):
n=len(a)
dp=[none]*n
dp[0]=a[0] #初始化,陣列起始元素,最大和是元素本身
result=0
for i in range(1,n):
dp[i]=max(dp[i-1],0)+a[i]
result = max(result,dp[i])
return result
if __name__ == '__main__':
s=solution()
#輸入以逗號分隔的陣列元素
str_in= input()
a = [int(n) for n in str_in.split(',')] #將輸入轉變成陣列
print(s.multiply(a))
'''爬樓梯問題
'''class solution:
def stairs(self , n):
dp=[none]*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]
if __name__ == '__main__':
s=solution()
print(s.stairs(4))
參考文章 動態規劃問題總結
一 什麼是動態規劃?動態規劃演算法通常基於乙個遞推公式及乙個或多個初始狀態。當前子問題的解將由上一次子問題的解推出。使用動態規劃來解題只需要多項式的時間複雜度。二 例子講解 首先,我們要找到某個狀態的最優解,然後在它的幫助下,找到下乙個狀態的最優解。用leetcode上的198題house robb...
動態規劃的問題總結
1 dp 個數 weight 例子 poj 1837 balance dp i w relate to dp i 1 w c j 這道題中dp儲存的不是weight,而是方法個數 include include include include include include define maxnu...
經典動態規劃問題總結
動態規劃引入 首先我們以乙個最基本的例子來分析 菲波那切數列。我們都知道,菲波那切數列的遞推公式f n f n 1 f n 2 這裡我就說明一般情況,不列舉邊界條件了 很簡單,如果我們用遞迴的方法來求解f n 兩三行 就出來了。那麼我們深入分析一下這樣有什麼問題?f 2 f 1 f 0 f 3 f ...