動態規劃系列之二最大和子陣列
動態規劃之最大和子陣列
給定一個整數陣列 nums ,找到一個具有最大和的子陣列(子陣列最少包含一個元素),返回其最大和
輸入: [-2,-1,-3,4,-1,2,1,-5]
輸出: 6
解釋: 連續子陣列 [4,-1,2,1]
的和最大,為 6。
解題思路
使用動態方程解題就需要題目符合動態方程解法,動態方程的兩個思路:
- 將大的問題拆解成小一點問題,小問題和大問題的解決思路是類似的
- 小問題之間的關係能完成大問題的最優解
建立數學模型
求陣列arr中最大和的子陣列,建立一個子陣列和的列表dp。其中dp[i]代表著以第i-1個元素結尾的陣列的最大和。
一般情況下,我們可以用dp[i]代表以第 i 個元素結尾的子陣列的最值,而遞推關係就是考慮和dp[i-1]之間的關係
邊界值
dp[i]代表這以第i-1個元素結尾的子陣列的最大和,所以dp[0]就是邊界值。當陣列只有一個元素時,該元素就是陣列的最大和
狀態轉移方程
狀態轉移方程是最難寫的,但是我們要迎難而上。下面分析一下該狀態如何轉移。
這裡用dp[i]代表以第 i 個元素結尾的子陣列的最大和,則max(dp[i])就是要求的最終結果,那麼關鍵是如何寫出遞推關係式。
轉化後模型:
求i=7結尾的最大和。求i=7時,要知道前面的前一個dp[7-1]的值。比較 dp[6] + nums[7] 和 nums[7] 的大小,大的那一個就是dp[7]的值,也就是以 第7個元素結尾的子陣列的最大值。
這裡求8個元素的最大和的子陣列,這個大問題可以拆解成小問題,即求8個元素的最大和的子陣列,繼續拆解可以為求7個元素的最大和的子陣列,一直到求1個元素的最大和子陣列。
-2,-1,-3,4,-1,2,1,-5
-2,-1,-3,4,-1,2,1
-2,-1,-3,4,-1,2
-2,-1,-3,4,-1
-2,-1,-3,4
-2,-1,-3
-2,-1
-2
小問題之間的聯絡,可以完成大問題的最優解。
-2 最大和就是-2
-2,-1 以-1結尾的最大和為-1,因為前i-1個元素+i元素的最大值為負數,所以最大值為當前-1
-2,-1,-3 以-3結尾的最大和為-3,前2個元素+i元素的最大值為-3,相比較當前值-3,最大值就是-3
-2,-1,-3,4 以4結尾的最大和為4,前3個元素的最大和-3為負數,但沒第4個元素,所以最大的和為4
-2,-1,-3,4,-1 以-1結尾的最大和為3,前4個元素的最大和為4,所以加上當前值能變大,最大和為3
-2,-1,-3,4,-1,2 以2結尾的最大和為5,前5個元素的最大和為3,所以加上當前值能變大,最大和為5
-2,-1,-3,4,-1,2,1 以1結尾的最大和為1,前6個元素的最大和為5,加上當前值為6,
-2,-1,-3,4,-1,2,1,-5 以-5結尾的最大和1,前7個元素的最大和為1,大於0,所以加上當前元素能變大,最大和為1
統計出所有以第i個元素結尾的最大和為
-2 | -1 | -3 | 4 | -1 | 2 | 1 | -5 |
---|---|---|---|---|---|---|---|
-2 | -1 | -3 | 4 | 3 | 5 | 6 | 1 |
可以看出當以1結尾時,可以獲得最大和的子陣列,值為6
明顯的,因為我們考慮的子陣列以nums[i]結尾,那麼該陣列一定是包含nums[i]這個元素的,因此需要考慮兩種情況:即nums[i]單獨成為一段還是與前面的dp[i-1]一起構成子陣列,因此,可以得到如下的遞推關係式:
dp[i]=max(dp[i-1]+nums[i],nums[i])
或者說:
dp[i-1]代表著前i-1個元素組成的子陣列的最大和,那麼加上第i個元素時就有兩種情況:
- 第i個元素 大於
dp[i-1]+arr[i]
,那麼前i個元素的最大值為第i個元素的值 - 第i個元素 小於
dp[i-1]+arr[i]
,那麼前i個元素的最大值為前i-1個元素的值 + 第i個元素
dp[i] = max{arr[i], dp[i-1]+arr[i]}
python程式碼
input_list = [-2,-1,-3,4,-1,2,1,-5]
length = len(input_list)
dp = [0] * length
# 邊界值,如果只有一個元素,則第一個元素的最大值就是自身
dp[0] = input_list[0]
for i in range(1,len(input_list)):
# if dp[i-1] + input_list[i] > input_list[i] ---> dp[i-1] > 0。或許前一種寫法更容易理解
if dp[i-1] > 0:
# 如果前i-1個元素的最大值大於0,那麼加上當前就能使得以當前值為結尾的最大和變大
dp[i] = dp[i-1] + input_list[i]
else:
# 如果前i-1個元素的最大值小於0,那麼加上當前就能使得以當前值為結尾的最大和變小。以當前值為結尾的最大和就是自身組成的陣列
dp[i] = input_list[i]
print(dp)
print(max(dp))
又可以寫成:
input_list = [-2,1,-3,4,-1,2,1,-5,4]
length = len(input_list)
# 構建一個dp陣列,用來儲存以第i個元素為結尾的最大子陣列之和
dp = [0] * length
# 邊界值,如果只有一個元素,則第一個元素的最大值就是自身
dp[0] = input_list[0]
for i in range(1,length):
# 迴圈陣列,比較前i-1個數組最大值+自身 和自身的大小。
# 如果大則表明前i-1個數組是正數,則可以繼續加上第i個
# 如果小則表明前i-1個數組是負數,那麼相加肯定更小,所以以自身為新起點繼續往下走
dp[i] = max(dp[i-1]+input_list[i],input_list[i])
print(max(dp))
小結
最長連續子序的解法精華在於 維護了一個dp陣列,該陣列中的每一個元素都代表著前i-1個元素能夠組成的最大值,只需要比較前i-1個元素+第i個元素的值與第i個元素的值的大小,就能得到第前i個元素能夠組成的子陣列的最大值。