《劍指offer31》:連續子數組的最大和

這是《劍指offer》系列的第一篇,這個系列的文章主要用於記錄看書時的一些感想,做個記錄。

 

題目

輸入一個整型數組,數組裏有正數也有負數。數組中一個或連續的多個整數組成一個子數組。求所有子數組的和的最大值。要求時間複雜度爲O(n)。例:

思路

這裏採用動態規劃的思想:

  • 第一步:做一個選擇,基於這個選擇產生一個或多個待解的子問題

這裏用函數 f(i) 表示以第 i 個數字結尾的子數組的最大和,其中 0\leq i< n。注意到函數 i 的不同取值構成了原題可能解的全集。例:當 i = 2 的時候,有 f(2) = 1 + (-2) + 3 = 2  。

這裏我們選擇子數組的結束位置 i = 6 ,並且假定這個選擇是最優解。即:我們假設結束於下標爲 6 的子數組的最大和是原數組中所有子數組的最大和。

選擇了結束位置之後,數組下標 6 的右邊不用再進行考慮。那麼基於 f(6) 產生子問題 f(6 - 1) ,意義是結束於下標爲  5 的子數組的最大和。

  • 第二步:證明問題具有最優子結構性質,並得到遞歸式

使用“剪切 - 粘貼”反證法,證明在 f(5)>0 的條件下問題具有最優子結構性質:

假設 f(5) 不是數組區間 Data[0, 5] 內結束於下標爲 5 的子數組的最大和,那麼我們將 f(5) 從 f(6) 中剪切去掉,並把最優解 f^*(5) 粘貼其中得到  f^*(6), 則有 f^*(6) > f(6),與“ f(6) 是最優解”矛盾,故原假設不成立,得證。

這裏稍微有點繞,如下圖:

這裏我們證明的是: f(5) 區間在 f(5)> 0 的時候一定會被f(6) 區間包含。

注:這裏約束 f(5)> 0 的意義在於,當子問題的解是負數的時候,即使它是子問題能達到的最大值,也會拖了原問題的後腿,還不如直接等於 Data[i] 。例如 f(2) 的子問題 f(1) = 1 + (-2) = -1​ ​​​​​​,那結束於下標爲 2 的子數組的最大和應該等於 Data[2] 本身,即 f(2) = 3 。這是來自於我們定義這個函數時隱含的約束 

由此我們證明了原問題的最優解 “ 結束於下標爲 6 的子數組的最大和 ” 包含了它的子問題 “ 結束於下標爲 5 的子數組的最大和 ” 的最優解。我們稱這個問題具有最優子結構。

得到以下遞推式:

f(i)=\left\{\begin{array}{ll} { \text {Data}[i]} & {i=0 \text { or } f(i-1) \leq 0} \\ {f(i-1)+\text {Data[i] }} & {i \neq 0 \text { and } f(i-1)>0} \end{array}\right.

  • 第三步:證明問題具有重疊子問題性質

如果遞歸算法反覆求解相同的子問題,我們稱最優化問題具有重疊子問題性質,這裏我們採用子問題圖來可視化子問題之間的依賴關係,其中節點代表子問題,弧代表調用關係:

例如規模爲4的子問題在求解時會調用規模爲3、2和1的子問題,而處理規模爲3的子問題的時候又會調用規模爲2和1的子問題,造成高昂的計算代價。

  • 第四步:使用動態規劃進行求解

動態規劃求解分爲帶備忘錄的自頂向下法帶表格的自底向上法。

以下附上劍指offer提供的自底向上法:

結語

動態規劃的題目常見,做多了題,有時候稀裏糊塗地套上去試一下能做出來。但一直不懂背後的原理。這篇主要是用《算法導論》裏面的分析手法分析了下《劍指offer》裏的題,得到了些粗淺的看法,以後還要多加練習。

 

Reference:

《算法導論》p216

《劍指offer》p171

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章