最大子段和 | 最大子矩陣 | 最大M子段和
最大子段和
給定由n個整數(可能為負整數)組成的序列
as = a0, a1, ... , a(n - 1)
,求該序列形如∑ak(k = l => r - 1)
的子段和的最大值。對於全是負數的陣列最大子段和為0。
簡單方法
給定這個題目,首先可以想到列舉每一對可能的左右邊界[l, r)
並計算其中的和sum[l, r)
,這樣一個方法可以實現O(n ^ 3)
的演算法。
觀察到這之中的重複計算,我們可以預處理一下,建立一個陣列,其中存放所有的sum[0, r)
,計算sum[l, r)
時利用sum[l, r) = sum[1, r) - sum[1, l)
,這樣可以將演算法優化到O(n ^ 2)
。(典型的空間換時間)
進一步分析
優化的重要思路是利用先前的計算(上面的預處理陣列是這一思想的極致),對於最優化問題,自然是思考如何利用子最優構造最優,也就是尋找最優子結構。
如果有最優解在區間[l, r)
。那麼,對於l <= m <= r
,我們可以發現sum[l, m) >= 0
。否則,sum[m, r) >= sum[l, r)
。同理:sum[m, r) >= 0
。
反之,對於任意1 <= m <= r
,[l, m)
的最優字首和[m, r)
的最優字尾構成bst[l, r)
。(最優子結構)
現在有原問題求bst[l, r)
,對於任意l < m < r
,我們可以分解問題為
bst[l, r) = max(bst[l, m), bst[m, r), [l, m)中的最優字尾+[m, r)中的最優字首)
上面驚現了標準的分治法,不過看上去非常複雜,我們要找到子解的最優,同時維持兩端最優資訊……
仔細看看,這是因為m可以是(l, r)
中的任意位置,如果把m選在r - 1
,就有
bst[l, r) = max(bst[l, r - 1), [l, r - 1)中的最優字尾+a[r - 1])
這個遞推公式最重要的優化是將l排除出了計算過程,也就是說求bst[l, r)
是r的變化過程,l是“常數”,有
bst(r) = max(bst(r - 1), maxSuffix(r - 1) + a[r - 1])
這個公式中的變數r是線性變化的,不過maxSuffix
情況如何還沒分析。
通過上面的討論我們知道,maxSuffix(r) >= 0
maxSuffix(r) = max(0, maxSuffix(r - 1) + a[r - 1])
彙總
maxSuffix(r) = 0 r = 0
maxSuffix(r) = max(0, maxSuffix(r - 1) + a[r - 1]) r > 0
bst(r) = 0 r = 0
bst(r) = max(bst(r - 1), maxSuffix(r)) r > 0
整理思路
可以重述上面的思路:
- 我們取每一個右端點r,計算最優字尾,其中的最大值即為最大和子區間
- 最優字尾
masSuffix(r)
可以通過maxSuffix(r - 1)
在常數時間算出 - 演算法的複雜度為O(n)
最重要的:演算法的遞推不是建立在最優值觀點上的,而是最優字尾上的,雖然兩者的值相同。
二維推廣:最大子矩陣
二維上的最大和子區域可以直接轉化為一維問題。
在列上列舉[l, r)
,將zip[i] = sum(m[i][k]) (l <= k < r)
看成是一個數列,就可以像上面那樣對zip求解了。
複雜度:O(a * b ^ 2)
多子段推廣:最大M子段和
給定
as[0, n)
和正整數m,求m個不相交的子段,使m個子段的總和最大。
直接從遞推出發
定義
dp[i][j] : 表示as[0, j)的i欄位和
oneSum[l, r) : 表示[l, r)上的最大單欄位和
有
dp[i][j] = max(dp[i - 1][k] + oneSum[k, j)) (i <= k < j)
通過上述遞推式可以寫出一個O(n ^ 4)
時間的演算法。
觀察可以發現oneSum上的O(n)
不是很必要,對於固定的l,可以打出整張oneSum[l, r)
表:每次固定一個l,O(n)
時間打出[l, r)
,總時間消耗O(n ^ 2)
。
現在可以在O(n ^ 3)
時間內求解了,不過還可以繼續優化。
接下來的優化比較複雜:仔細觀察上面的遞推式,可以發現對於同一個i,j的求值是沒有相互關聯的。這給了我們一個訊號,我們能否發現j們的相互關係?
視線回到上方“整理思路”的部分,當時我們通過轉換視角建立了遞推方程。這裡呢?
對於固定的i,我們希望通過遞推的方式求出j,即[j-x] → [j]
考慮dp[i][j]
和dp[i][j - 1]
的關係,借鑑一維時的思路,我們有
temp[j] = max(dp[i - 1][k]) (k <= j)
dp[i][j] = max(temp[j - 1], dp[i][j - 1]) + as[j - 1]
temp[j - 1]
: 表示取dp[i - 1][k < j]
中最優的做前i - 1段,第i段從j處另起dp[i][j - 1]
: 表示繼承dp[i][j - 1]
流傳下來的第i段temp[j]
可在計算dp[i - 1]
時計算,不增加複雜度這裡我一開始的思路里並沒有引入temp表,而是希望通過
dp[i - 1][j - 1]
來表示dp[i - 1]
段最優子解,不過後來發覺dp[i - 1][j - 1]
不一定具有temp[j - 1]
的最優性,而且還無法表示跳躍(相鄰兩段之間有沒有納入子段中的部分)。才回頭看書上的步驟。
回頭看,這一遞推式的思路與maxSuffix
是何其相似。
結語
最大M欄位問題一直是我智商不能抵達的地方,雖然每次跟著書上走可以大概理解,卻總是離不了書。這次獨立走的最遠,只有一點小錯誤(大麻煩)。希望這次算是真正理解了。
相關文章
- 最大連續子陣列和(最大子段和)陣列
- 【51nod】最大子段和
- 山海經:線段樹維護最大子段和
- #1502 : 最大子矩陣矩陣
- 動態規劃求解最大子段和 (兩種寫法+還原最優解)動態規劃
- 51nod 1254 最大子段和 V2
- 53. 最大子陣列和陣列
- LeetCode53. 最大子陣列和LeetCode陣列
- [Python手撕]最大子陣列和Python陣列
- 動態規劃---例題3.最大子段和問題動態規劃
- 【力扣】最大子陣列和(貪心)力扣陣列
- Hetao P1156 最大戰力 題解 [ 綠 ][ 二分 ][ 最大子段和 ]
- 順序表應用7:最大子段和之分治遞迴法遞迴
- 分治法 && 動態規劃 洛谷P1115 最大子段和動態規劃
- 順序表應用8:最大子段和之動態規劃法動態規劃
- 最大子列和問題
- leetcode_最大子序和LeetCode
- 求二維陣列中最大子陣列的和陣列
- 最大雙子段和
- 洛谷題單指南-常見最佳化技巧-P1115 最大子段和
- 【刷題筆記】LeetCode-53 最大子陣列和筆記LeetCode陣列
- 【DP】乘積最大子陣列陣列
- [-演算法篇-] 最大子序列和演算法
- 第四章:多維陣列和矩陣 ------------- 4.4 找出邊界為1的最大子方陣陣列矩陣
- 53. 最大子序和(python3)Python
- 最大子陣列問題(Maximum subarray problem)陣列
- 53_Maximum Subarray-最大子陣列陣列
- 【Leetcode】152.乘積最大子陣列LeetCode陣列
- LeetCode-152-乘積最大子陣列LeetCode陣列
- 53. 最大子序和(劍指 Offer 42)
- 2021-01-03 | 53. 最大子序和
- 【刷演算法】LeetCode.53-最大子序和演算法LeetCode
- 分治法演算法學習(一)——歸併排序、求最大子陣列和演算法排序陣列
- 第四章:多維陣列和矩陣 ------------- 4.8 子矩陣的最大累加和陣列矩陣
- 從0打卡leetcode之day 3 -- 最大子序列和LeetCode
- leetcode 152 乘積最大子序列LeetCode
- 求最大子序列的問題
- 廣州C++信奧老師解一本通題 1260:1282:最大子矩陣C++矩陣
- 【LeetCode】【分治法】連續數列(最大子序和)思路解析和程式碼LeetCode