最大子陣列問題(第4章 分治策略)

2021-06-21 12:11:54 字數 2801 閱讀 4129

求子陣列的最大和

題目描述:

輸入乙個整形陣列,陣列裡有正數也有負數。

陣列中連續的乙個或多個整數組成乙個子陣列,每個子陣列都有乙個和。

求所有子陣列的和的最大值。要求時間複雜度為o(n)。

例如輸入的陣列為1, -2, 3, 10, -4, 7, 2, -5,和最大的子陣列為3, 10, -4, 7, 2,

因此輸出為該子陣列的和18。

思路1 當我們加上乙個正數時,和會增加;當我們加上乙個負數時,和會減少。如果當前得到的和是個負數,那麼這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。實現:

//

//updated,2011.05.25.

#include int maxsum(int* a, int

n)

return

sum;

}

intmain()

;

//int a=;

//測試全是負數的用例

cout<8)<

return

0;

}

/*-------------------------------------

解釋下:

例如輸入的陣列為1, -2, 3, 10, -4, 7, 2, -5,

那麼最大的子陣列為3, 10, -4, 7, 2,

因此輸出為該子陣列的和18。

所有的東西都在以下倆行,

即: b : 0 1 -1 3 13 9 16 18 13

sum: 0 1 1 3 13 13 16 18 18

其實演算法很簡單,當前面的幾個數,加起來後,b<0後,

把b重新賦值,置為下乙個元素,b=a[i]。

當b>sum,則更新sum=b;

若b

據說這道題是《程式設計珠機》裡面的題目,叫做掃瞄法,速度最快,掃瞄一次就求出結果,複雜度是o(n)。書中說,這個演算法是乙個統計學家提出的。

這個演算法如此精煉簡單,而且複雜度只有線性。但是我想,能想出來卻非常困難,而且證明也不簡單。在這裡,我斗膽寫出自己證明的想法:

關於這道題的證明,我的思路是去證明這樣的掃瞄法包含了所有n^2種情況,即所有未顯示列出的子陣列都可以在本題的掃瞄過程中被拋棄。

1 首先,假設演算法掃瞄到某個地方時,始終未出現加和小於等於0的情況。

我們可以把所有子陣列(實際上為當前掃瞄過的元素所組成的子陣列)列為三種:

1.1 以開頭元素為開頭,結尾為任一的子陣列

1.2 以結尾元素為結尾,開頭為任一的子陣列

1.3 開頭和結尾都不等於當前開頭結尾的所有子陣列

1.1由於遍歷過程中已經掃瞄,所以演算法已經考慮了。1.2確實沒考慮,但我們隨便找到1.2中的某乙個陣列,可知,從開頭元素到這個1.2中的陣列的加和大於0(因為如果小於0就說明掃瞄過程中遇到小於0的情況,不包括在大前提1之內),那麼這個和一定小於從開頭到這個1.2陣列結尾的和。故此種情況可捨棄

1.3 可以以1.2同樣的方法證明,因為我們的結尾已經列舉了所有的情況,那麼每一種情況和1.2是相同的,故也可以捨棄。

2 如果當前加和出現小於等於0的情況,且是第一次出現,可知前面所有的情況加和都不為0

乙個很直觀的結論是,如果子段和小於0,我們可以拋棄,但問題是是不是他的所有以此子段結尾為結尾而開頭任意的子段也需要拋棄呢?

答案是肯定的。因為以此子段開頭為開頭而結尾任意的子段加和都大於0(情況2的前提),所以這些子段的和是小於當前子段的,也就是小於0的,對於後面也是需要拋棄的。也就是說,所有以之前的所有元素為開頭而以當前結尾之後元素為結尾的陣列都可以拋棄了。

而對於後面拋棄後的陣列,則可以同樣遞迴地用1 2兩個大情況進行分析,於是得證。

這個演算法的證明有些複雜,現在感覺應該不會錯,至少思路是對的,誰幫著在表達上優化下吧。:-)

2 動態規劃:設sum[i] 為前i個元素中,包含第i個元素且和最大的連續子陣列,result 為已找到的子陣列中和最大的。對第i+1個元素有兩種選擇:做為新子陣列的第乙個元素、放入前面找到的子陣列。

sum[i+1] = max(a[i+1], sum[i] + a[i+1])

result = max(result, sum[i])

(曾經在某個部落格中看的有乙個人給出的動態規劃思路有一些錯誤之處,但思路還是對的,錯誤之處可能是疏漏吧,結果旁人就撻伐之,這種態度很不好)

3 分治,合併的時候窮舉即可

//

algorithm 3:時間效率為o(n*log n)

//演算法3的主要思想:採用二分策略,將序列分成左右兩份。

//那麼最長子序列有三種可能出現的情況,即

//只出現在左部分.

//只出現在右部分。

//出現在中間,同時涉及到左右兩部分。

//分情況討論之。

static

int maxsubsum(const

int a,int left,int

right)

maxrightbordersum=0

; rightbordersum=0

;

for(i=center+1;i<=right;i++)

int max1=maxleftsum>maxrightsum?maxleftsum:maxrightsum;

int max2=maxleftbordersum+maxrightbordersum;

return max1>max2?max1:max2;

}

第4章 分治策略 最大字陣列問題

這一章主要給我們介紹了分治演算法,這演算法由如下三個步驟組成 分解步驟將問題劃分為一些子問題,子問題的形式與原問題一樣,只是規模更小 解決步驟遞迴地求解這些子問題,如果子問題的規模足夠小,則停止遞迴,直接求解 合併步驟將子問題的解組合成原問題的解。t n at n b f n a 1,b 1 當我們...

分治策略之最大子陣列問題

問題 乙個整數陣列中的元素有正有負,在該陣列中找出乙個連續子陣列,要求該連續子陣列中各元素的和最大,這個連續子陣列便被稱作最大連續子陣列。比如陣列的最大連續子陣列為,最大連續子陣列的和為5 2 1 2 8。一 暴力解法 include using namespace std class soluti...

分治策略之最大子陣列問題

最大子陣列,即子陣列中的各個元素相加的和是所有子陣列中最大的。假設最大子陣列為 ai.aj 則必然是以下三種情況 1 完全位於子陣列a low.mid 中 2 完全位於子陣列a mid 1.high 中 3 跨越了中點low i mid j high 如果暴力求解的話,時間複雜度為 n int fi...