问题描述
输入一组整数,求出这组数字子序列和中最大值。也就是只要求出最大子序列的和,不必求出最大的那个序列。例如:
序列:-2 11 -4 13 -5 -2,则最大子序列和为20。
复杂度为O(N^2)的算法
#include <stdio.h> #include <stdlib.h> int MaxSubSeqSum(const int *A, int N, int *start, int *end) { int i = 0; int j = 0; int cur_sum = 0; int Max_sum = 0; for(i = 0; i < N; i ++) { cur_sum = 0; for(j = i; j < N; j ++) { cur_sum += A[j]; if(cur_sum > Max_sum) { Max_sum = cur_sum; *start = i; *end = j; } } } return Max_sum; } int main() { int A[] = {-2,11,-4,13,-5,-2}; int start_index, end_index; int max = MaxSubSeqSum(A,sizeof(A)/sizeof(A[0]),&start_index,&end_index); printf("MaxSubSeqSum is %d -- %d \n",A[start_index],A[end_index]); printf("max:%d \n",max); }
采用的是“分治“(divide-and-conquer)策略。思想是把问题分成两个大致相当的子问题,然后递归地对他们求解,这是”分“。”治“阶段将两个子问题的解合并到一起,可能再做一些附加的工作,最终得到整个问题的解。
上述问题,把序列分为两部分,最大子序列可能出现在左半部分,或者右半部分,或者是两者之间。两者之间的情况下,先对左半部分求以最后一个数字为结尾的最大序列和。然后对右半部分以第一个数字开始算最大序列和,将两者加起来即是。
int Max_3(int a, int b, int c) { if(a < b) a = b; if(a < c) return c; else return a; } int MaxSubSeqSum2(const int *A, int left, int right) { int MaxLeftSum, MaxRightSum, MaxSum; int MaxLeftBorderSum, MaxRightBorderSum; int LeftBorderSum, RightBorderSum; int center; int i; if( left == right) { if(A[left] > 0) return A[left]; else return 0; } center = (left + right) / 2; MaxLeftSum = MaxSubSeqSum2(A,left,center); MaxRightSum = MaxSubSeqSum2(A,center + 1,right); MaxLeftBorderSum = 0; LeftBorderSum = 0; for(i = center; i >= left; i--) { LeftBorderSum += A[i]; if(LeftBorderSum > MaxLeftBorderSum) MaxLeftBorderSum = LeftBorderSum; } MaxRightBorderSum = 0; RightBorderSum = 0; for(i = center + 1; i <= right; i++) { RightBorderSum += A[i]; if(RightBorderSum > MaxRightBorderSum) MaxRightBorderSum = RightBorderSum; } MaxSum = Max_3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum); return MaxSum; }
算法复杂度为O(N)的算法
/* 如果a[i]为负数,那么它不可能代表最优序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过 用a[i+1]作为起点而得到改进。同理,任何小于零的子序列不可能是最优子序列的前缀。*/ int MaxSubSeqSum3(const int *A, int N, int *start, int *end) { int i = 0; int j = 0; int cur_sum = 0; int Max_sum = 0; for(i = 0; i < N; i ++) { cur_sum += A[i]; if(cur_sum > Max_sum) { Max_sum = cur_sum; *end = i; } else if(cur_sum < 0) { cur_sum = 0; j = i + 1; } if(j <= *end) *start = j; } return Max_sum; } int main() { int A[] = {-2,11,-4,13,-5,-2}; int start_index, end_index; int max = MaxSubSeqSum3(A,sizeof(A)/sizeof(A[0]),&start_index,&end_index); printf("MaxSubSeqSum is %d -- %d \n",A[start_index],A[end_index]); printf("max:%d \n",max); }