1. 程式人生 > >最大子序和(DP,分治)

最大子序和(DP,分治)

 


給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:

如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。

 

以a[0]結尾的子序列只有a[0]
以a[1]結尾的子序列有 a[0]a[1]和a[1]
以a[2]結尾的子序列有 a[0]a[1]a[2] / a[1]a[2] / a[2]
……
以a[i]結尾的子序列有a[0]a[1]……a[i-2]a[i-1]a[i]  / a[1]a[2]……a[i-2]a[i-1]a[i] /  
a[2]a[3]……a[i-2]a[i-1]a[i] / …… /  a[i-1]a[i] / a[i]

所有以a[0] ~a[n]結尾的子序列分組構成了整個序列的所有子序列。
這樣,我們只需求以a[0]~a[n]結尾的這些分組的子序列中的每一分組的最大子序列和。然後從n個分組最大子
序列和中選出整個序列的最大子序列和。
觀察可以發現,0,1,2,……,n結尾的分組中,
maxsum a[0] = a[0]
maxsum a[1] = max( a[0] + a[1] ,a[1])  = max( maxsum a[0] + a[1] ,a[1]) 
maxsum a[2] = max( max ( a[0] + a[1] + a[2],a[1] + a[2] ),a[2])  
= max(  max( a[0] + a[1] ,a[1]) + a[2] , a[2]) 
= max(  maxsum a[1] + a[2] , a[2])
……

依此類推,可以得出通用的式子。

maxsum a[i] = max( maxsum a[i-1] + a[i],a[i])


只要上一個子序列最大和為正,那麼無論當前值的正負,都會與當前的相加,這樣以當前值結尾的子序列最大
和就會增大。(一個正數 加一個 正數2 或者負數 那麼都會比這個正數2 或負數原來要增大,同理,一個
負數加任何一個數,都會使這個數減小,因此當前一子序列最大和小於零時,我們就歸零它了,相當於是不
加任何數,而保留當前位置值本身)

1.子序列必然以正數開頭
2.一個子序列的前面的部分子序列之和必然為正數,若為負數就需要去掉
class Solution {
    static int max;
    public static int maxSubArray(int[] a) {
        int[] dp = new int[a.length];
        max = dp[0]=a[0];
        for (int i = 1; i < a.length; i++) {
            if (dp[i - 1] + a[i] > a[i]) {
                dp[i] = dp[i - 1] + a[i];
            }else{
                dp[i] = a[i];
            }
            if(max<dp[i]){
                max=dp[i];
            }

        }
        return max;
    }
}

另外一種思路,分治法

子序列分為 左邊  右邊   跨左右

static int MaxSubSum(int A[] ,int left,int right){
    int MaxLeftSum,MaxRightSum;
    int MaxLeftBorderSum,MaxRightBorderSum;//包含了 左邊的最右邊的元素 的最大值 ,右邊同理
    int LeftBorderSum,RightBorderSum;
    if(left==right){
        return A[left];
    }
    int  mid = ( left + right ) / 2;
    MaxLeftSum = MaxSubSum(A,left,mid);
    MaxRightSum = MaxSubSum(A,mid+1,right);
    
    for(int i= mid ; i>=left;i--){
        LeftBorderSum +=A[i];
        if(LeftBorderSum >MaxLeftBorderSum){
            MaxLeftBorderSum = LeftBorderSum ;
        }
    }

    for(int i= mid +1 ; i<=right;i++){
        RightBorderSum += A[i];
        if(RightBorderSum>MaxRightBorderSum){
            MaxRightBorderSum= RightBorderSum;
        }
    }

return Math.max(Math.max(MaxLeftSum ,MaxRightSum ),MaxRightBorderSum+MaxLeftBorderSum );
}