算法設計與分析——最大子段和(分治)


一、問題描述

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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM