來源:http://blog.csdn.net/luxiaoxun/article/details/7438315
問題:
給定一整數序列A1, A2,... An (可能有負數),求A1~An的一個子序列Ai~Aj,使得Ai到Aj的和最大
例如:整數序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和為21。對於這個問題,最簡單也是最容易想到的那就是窮舉所有子序列的方法。利用三重循環,依次求出所有子序列的和然后取最大的那個。當然算法復雜度會達到O(n^3)。

1 int maxSubSum1(int a[],int size ) 2 { 3 int maxSum = 0; 4 for ( int i = 0; i < size; i++ ) 5 for ( int j = 1; j < size; j++ ) 6 { 7 int thisSum = 0; 8 for ( int k = i; k <= j; k++ ) 9 thisSum += a[k]; 10 if ( thisSum > maxSum ) 11 maxSum = thisSum; 12 } 13 return maxSum; 14 }
這個算法很簡單,i表示子序列起始下標,j表示子序列結束下標,遍歷子序列的開頭和結束下標,計算子序列的和,然后判斷最大子序列。很明顯的看出算法復雜度是O( pow( n, 3 ) )
顯然這種方法不是最優的,下面給出一個算法復雜度為O(n)的線性算法實現,算法的來源於Programming Pearls一書。在給出線性算法之前,先來看一個對窮舉算法進行優化的算法,它的算法復雜度為O(n^2)。其實這個算法只是對對窮舉算法稍微做了一些修改:其實子序列的和我們並不需要每次都重新計算一遍。假設Sum(i, j)是A[i] ... A[j]的和,那么Sum(i, j+1) = Sum(i, j) + A[j+1]。利用這一個遞推,我們就可以得到下面這個算法:

1 int max_sub(int a[],int size) 2 { 3 int i,j,v; 4 int max=a[0]; 5 for(i=0;i<size;i++) 6 { 7 v=0;//開始以下一個i開頭時要對上一個i開頭的累加和清零。 8 for(j=i;j<size;j++) 9 { 10 v=v+a[j]; //Sum(i, j+1) = Sum(i, j) + A[j+1] 11 if(v>max) max=v; 12 } 13 } 14 return max; 15 }
那怎樣才能達到線性復雜度呢?這里運用動態規划的思想。先看一下源代碼實現:
(PS:嗯這個動態規划的代碼還真沒看懂……)
int max_sub2(int a[], int size) { int i,max=0,temp_sum=0; for(i=0;i<size;i++) { temp_sum+=a[i]; if(temp_sum>max) max=temp_sum; else if(temp_sum<0) temp_sum=0; } return max; }
在這一遍掃描數組當中,從左到右記錄當前子序列的和temp_sum,若這個和不斷增加,那么最大子序列的和max也不斷增加(不斷更新max)。如果往前掃描中遇到負數,那么當前子序列的和將會減小。此時temp_sum 將會小於max,當然max也就不更新。如果temp_sum降到0時,說明前面已經掃描的那一段就可以拋棄了,這時將temp_sum置為0。然后,temp_sum將從后面開始將這個子段進行分析,若有比當前max大的子段,繼續更新max。這樣一趟掃描結果也就出來了。
分治法:最大子序列和可能出現在三個地方:整個出現在輸入數據的左半部分,整個出現在輸入數據的右半部分,或者跨越輸入數據的中部從而占據左右兩個半部分。
(PS:原文的這段分治法的代碼看懂了個大概,后面附上另外兩段分治法的代碼吧。
另外,不管怎寫,分治法解決這個題目,時間復雜度都是O(nlogn)級別的。)
/** * Recursive maximum contiguous subsequence sum algorithm. * Finds maximum sum in subarray spanning a[left..right]. * Does not attempt to maintain actual best sequence. */ int maxSumRec( const vector<int> & a, int left, int right ) { if( left == right ) // Base case if( a[ left ] > 0 ) return a[ left ]; else return 0; int center = ( left + right ) / 2; int maxLeftSum = maxSumRec( a, left, center ); int maxRightSum = maxSumRec( a, center + 1, right ); int maxLeftBorderSum = 0, leftBorderSum = 0; for( int i = center; i >= left; i-- ) { leftBorderSum += a[ i ]; if( leftBorderSum > maxLeftBorderSum ) maxLeftBorderSum = leftBorderSum; } int maxRightBorderSum = 0, rightBorderSum = 0; for( int j = center + 1; j <= right; j++ ) { rightBorderSum += a[ j ]; if( rightBorderSum > maxRightBorderSum ) maxRightBorderSum = rightBorderSum; } return max3( maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum ); } /** * Driver for divide-and-conquer maximum contiguous * subsequence sum algorithm. */ int maxSubSum3( const vector<int> & a ) { return maxSumRec( a, 0, a.size( ) - 1 ); }
下面的代碼來自http://www.2cto.com/kf/201311/255297.html

1 #include <stdio.h> 2 int Max2(int a, int b) 3 { 4 return a>b ? a:b; 5 } 6 int Max3(int a, int b, int c) 7 { 8 int max = a; 9 if(max < b) max = b; 10 if(max < c) max = c; 11 return max; 12 } 13 int MaxSubSum(int* a, int fI, int lI) 14 { 15 int i = 0; 16 int mI = (fI+lI) >> 1; 17 int lMax = 0, rMax = 0, lMaxBorder = 0, rMaxBorder = 0, lSumBorder = 0, rSumBorder = 0, max = 0; 18 //so how about 19 if(fI == lI) 20 return *(a+fI); 21 22 lMax = MaxSubSum(a, fI, mI); 23 rMax = MaxSubSum(a, mI+1, lI); 24 for(i=mI;i>=fI;i--) 25 { 26 lSumBorder += *(a+i); 27 if(lMaxBorder < lSumBorder) 28 lMaxBorder = lSumBorder; 29 } 30 for(i=mI+1;i<=lI;i++) 31 { 32 rSumBorder += *(a+i); 33 if(rMaxBorder < rSumBorder) 34 rMaxBorder = rSumBorder; 35 } 36 max = Max3(lMax, rMax, lMaxBorder+rMaxBorder); 37 return max; 38 } 39 40 int main() 41 { 42 int a[9] = {4,-3,5,-2,-1,2,6,-2,3}; 43 int L = sizeof(a) / sizeof(int); 44 int max = MaxSubSum(a, 0, L-1); 45 printf("%d \n", max); 46 return 0; 47 48 }
下面是參考劉汝佳的《書法競賽入門經典》page141 的代碼:

1 int maxSum(int *A,int x,int y)//返回數組A在左閉右開區間[x,y)中的最大連續和 2 { 3 int i,m,v,Lmax,Rmax,L,R; 4 int ans; 5 if(y-x==1) return A[x]; //假如只有一個元素,直接返回 6 m=x+(y-x)/2; //分治第一步:划分成[x,m)和[m,y) 7 Lmax=maxSum(A,x,m); //分治第二步:遞歸求解 8 Rmax=maxSum(A,m,y); 9 10 v=0; //分治第三步:合並(1)——尋找從分界點向左的最大連續和L 11 L=A[m-1]; 12 for(i=m-1;i>=x;i--) 13 { 14 v+=A[i]; 15 if(v>L) L=v; 16 } 17 18 v=0; //分治第三步:合並(2)——尋找從分界點向右的最大連續和R 19 R=A[m]; 20 for(i=m;i<y;i++) 21 { 22 v=v+A[i]; 23 if(v>R) R=V; 24 } 25 //把子問題的解與L+R 比較從而得到原問題的解。 26 ans=(Lmax>Rmax ? Lmax : Rmax); 27 return ans>(L+R)? ans:(L=R); 28 }
劉大師的代碼就是簡潔又給力呵呵