一、問題描述
Description
給定有n個整數(可能為負整數)組成的序列a1,a2,...,an,求該序列連續的子段和的最大值。 如果該子段的所有元素和是負整數時定義其最大子段和為0。
Input
第一行有一個正整數n(n<1000),后面跟n個整數,絕對值都小於10000。直到文件結束。
Output
輸出它的最大子段和。
Sample Input
6 -2 11 -4 13 -5 -2
Sample Output
20
解決該問題有很多方法可以通過暴力、動態規划和分治,這里使用分治的方法來解決該題目。
二、分治策略
用分治法求解這個問題 。
在數組的 center = (right-left)/2+left 位置處分開。形成兩個子數組。
那么,最大子段和 可能出現在三個位置:
a.可能出現在 左 子數組
b. 可能出現在 右子數組
c.可能出現在 過center的 中間某部分 元素 組成的子數組。
下面考慮 三種情況的計算方法:
第一種情況: 計算 left 到 center 的最大和,記作 leftSum
第二種情況: 計算從 center+1 到 right的最大和,記作 rightSum
第三種情況: 跨邊界的和。 以center為中心分別向兩邊計算和。
a.從 center出發,每次向左邊擴張一步,並且記錄當前的值S1,如果當前的和比上次的和大,就更新S1,一直向左擴張到位置 Left。
b.從 center+1出發,每次擴張一步,計算當前的和 為S2,如果當前的值比上次的和 大,那么,就更新S2的值,一直向右擴張到位置Right。
c.計算過center的連續值的和,S1+S2的值 Sum。 這個就是跨邊界的和。
上面三種情況考慮計算完成后,最后一步就是,比較三個值中的最大值,取最大值就可以了。
時間復雜度
我們在(left+right)/2處,把大問題分成了兩個部分的小問題。
T(N)1=2T(N)
T(N)2=N
在跨邊界求和的時候,我們需要計算 從center出發的到 left方向的最大和,每次增加一個元素,通過比較,得到最大的和,時間復雜度為O(N),同樣, 從center+1 出發的到 right 方向的最大和,每次增加一個元素,通過比較,得到最大的和,時間復雜度 為 O(N) ,所以,時間復雜度為 O(N)。
偽代碼
Java源碼
import java.util.*; public class Main { public static int []a =new int[1010]; public static int maxsum(int l,int r){ if(l==r){ return a[l]; } int mid = (l+r)/2; int lsum = maxsum(l,mid);//左區間 int rsum = maxsum(mid+1,r);//右區間 int sum1=0,sum2=0; int lefts=0,rights=0; for(int i=mid;i>=l;i--){ lefts+=a[i]; if(lefts>sum1){ sum1=lefts; } } for(int i=mid+1;i<=r;i++){ rights+=a[i]; if(rights>sum2){ sum2=rights; } } int msum=sum1+sum2; return Math.max(Math.max(lsum,rsum),msum); } public static void main(String[] args){ int n; int ans; Scanner in=new Scanner(System.in); while(in.hasNext()){ n=in.nextInt(); for(int i=1;i<=n;i++){ a[i]=in.nextInt(); } ans = maxsum(0,n); if(ans<0){ ans=0; } System.out.println(ans); } } }
C++源碼
#include<cstdio> #include<algorithm> using namespace std; int a[110]; int maxsum(int l,int r) { if(l==r) { return a[l]; } int mid = (l+r)/2; int lsum = maxsum(l,mid); //左區間 int rsum = maxsum(mid+1,r);//右區間 int sum1=0,sum2=0; int lefts=0,rights=0; //跨界求和 for(int i=mid; i>=l; i--) { lefts+=a[i]; if(lefts>sum1) { sum1=lefts; } } for(int i=mid+1; i<=r; i++) { rights+=a[i]; if(rights>sum2) { sum2=rights; } } int msum=sum1+sum2; return max(msum,max(lsum,rsum)); //三者中的最大值 } int main() { int n; int ans; while(scanf("%d",&n)!=EOF) { for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } ans = maxsum(0,n); if(ans<0) { ans=0; } printf("%d\n",ans); } }