最大子序和(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 );
}

猜你喜欢

转载自blog.csdn.net/qmylzx/article/details/83344019
今日推荐