最大子段和問題(maximum interval sum)
(有時也稱lis)
經典的動態規劃問題,幾乎所有的演算法教材都會提到.本文將分析最大子段和問題的幾種不同效率的解法,以及最大子段和問題的擴充套件和運用.
一.問題描述
給定長度為n的整數序列,a[1…n], 求[1,n]某個子區間[i , j]使得a[i]+…+a[j]和最大.或者求出最大的這個和.例如(-2,11,-4,13,-5,2)的最大子段和為20,所求子區間為[2,4].
二. 問題分析
1.窮舉法
窮舉應當是每個人都要學會的一種方式,這裡實際上是要窮舉所有的[1,n]之間的區間,所以我們用兩重迴圈,可以很輕易地做到遍歷所有子區間,乙個表示起始位置,乙個表示終點位置.**如下:
int start = 0
;//起始位置
int end = 0; //結束位置
int max = 0;
for(int i = 1; i <= n; ++i)
} }
這個演算法是幾乎所有人都能想到的,它所需要的計算時間是o(n^3).當然,這個**還可以做點優化,實際上我們並不需要每次都重新從起始位置求和加到終點位置.可以充分利用之前的計算結果.
或者我們換一種窮舉思路,對於起點 i,我們遍歷所有長度為1,2,…,n-i+1的子區間和,以求得和最大的乙個.這樣也遍歷了所有的起點的不同長度的子區間,同時,對於相同起點的不同長度的子區間,可以利用前面的計算結果來計算後面的.
比如,i為起點長度為2的子區間和就等於長度為1的子區間的和+a[i+1]即可,這樣就省掉了乙個迴圈,計算時間複雜度減少到了o(n^2).**如下:
int start = 0
;//起始位置
int end = 0;//結束位置
int max = 0;
for(int i = 1; i <= n; ++i)
} }
2.分治法
求子區間及最大和,從結構上是非常適合分治法的,因為所有子區間[start, end]只可能有以下三種可能性:
在[1, n/2]這個區域內
在[n/2+1, n]這個區域內
起點位於[1,n/2],終點位於[n/2+1,n]內
以上三種情形的最大者,即為所求. 前兩種情形符合子問題遞迴特性,所以遞迴可以求出. 對於第三種情形,則需要單獨處理. 第三種情形必然包括了n/2和n/2+1兩個位置,這樣就可以利用第二種窮舉的思路求出:
以n/2為終點,往左移動擴張,求出和最大的乙個left_max
以n/2+1為起點,往右移動擴張,求出和最大的乙個right_max
left_max+right_max是第三種情況可能的最大值
示例:
int maxinterval(int *a, int left, int right)
//center+1開始向右移動
sum = 0;
int right_max = 0;
for(int i = center+1; i <= right; ++i)
int ret = left_max+right_max;
if(ret < leftmaxinterval)
ret = leftmaxinterval;
if(ret < rightmaxinterval)
ret = rightmaxinterval;
return ret;
}
分治法的難點在於第三種情形的理解,這裡應該抓住第三種情形的特點,也就是中間有兩個定點,然後分別往兩個方向擴張,以遍歷所有屬於第三種情形的子區間,求的最大的乙個,如果要求得具體的區間,稍微對上述**做點修改即可. 分治法的計算時間複雜度為o(nlogn).
3.動態規劃法
動態規劃的基本原理這裡不再贅述,主要討論這個問題的建模過程和子問題結構.時刻記住乙個前提,這裡是連續的區間
令b[j]表示以位置 j 為終點的所有子區間中和最大的乙個
子問題:如j為終點的最大子區間包含了位置j-1,則以j-1為終點的最大子區間必然包括在其中
如果b[j-1] >0, 那麼顯然b[j] = b[j-1] + a[j],用之前最大的乙個加上a[j]即可,因為a[j]必須包含
如果b[j-1]<=0,那麼b[j] = a[j] ,因為既然最大,前面的負數必然不能使你更大
對於這種子問題結構和最優化問題的證明,可以參考演算法導論上的「剪下法」,即如果不包括子問題的最優解,把你假設的解粘帖上去,會得出子問題的最優化矛盾.證明如下:
令a[x,y]表示a[x]+…+a[y] , y>=x
假設以j為終點的最大子區間 [s, j] 包含了j-1這個位置,以j-1為終點的最大子區間[ r, j-1]並不包含其中
即假設[r,j-1]不是[s,j]的子區間
存在s使得a[s, j-1]+a[j]為以j為終點的最大子段和,這裡的 r != s
由於[r, j -1]是最優解, 所以a[s,j-1]
int
max = 0;
int b[n+1];
int start = 0;
int end = 0;
memset(b,0,n+1);
for(int i = 1; i <= n; ++i)
else
if(b[i]>max)
max = b[i];
}
動態規劃法的計算時間複雜度為o(n),是最優的解。做幾道題加深理解
最直白的lis題:
最大子段和公升級版,最大m段和:
最大子段和詳解
問題的提出 給定n個整數 可能為負數 組成的序列a 1 a 2 a 3 a n 求該序列如a i a i 1 a j 的子段和的最大值。當所給的整均為負數時定義子段和為0,依此定義,所求的最優值為 max,1 i j n 例如,當 a1,a2,a3,a4,a4,a6 2,11,4,13,5,2 時,...
最大子段和問題 詳解(C
最大子段和或稱為最大部分和 maximum subtotal 問題,以下簡稱ms。舉例說明 例如 暴力破解 brute force 可以從串的任意位置開始在連續的任意位置結束,一層迴圈控制起始位置,一層迴圈控制結束位置。複雜度 o n 2 迭代 include include using names...
最大子段和
設a 是n個整數的序列,稱為該序列的子串行,其中1 i j n.子串行的元素之和稱為a的子段和.例如,a 2,11,4,13,5,2 那麼它的子段和是 長度為1的子段和 2,11,4,13,5,2 長度為2的子段和 9,7,9,8,7 長度為3的子段和 5,20,4,6 長度為4的子段和 18,15...