最大子序和(DP,分治)
阿新 • • 發佈:2018-11-03
給定一個整數陣列 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 ); }