问题描述:
给定n个整数(可能为负数)组成的序列a[1], a[2], a[3],…, a[n], 求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整均为负数时定义子段和为0,
例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。
思路分析:
一、蛮力算法:
该算法思想是枚举的思想,将所有的子段和求一遍,依次比较,将子段和大的作为结果返回。实现该算法思想主要是找到子段左右两端的下标(i,j)。该算法可以改进:由于(a[i]+a[i+1]+…+a[j]) = a[j]+( a[i]+a[i+1]+…+a[j-1]),
避免重复计算,用变量thissum记录( a[i]+a[i+1]+…+a[j-1])的值。
改进前时间复杂度:O(n^3)
改进后时间复杂度:O(n^2)
二、分治法:
将数组a[n] 分为a[1..n/2]和a[n/2+1..n],分别求两端的最大子段和,a[1..n]的最大子段和有三种情况:
- a[n]的最大子段和与a[1..n/2]的最大子段和相同。
- a[n]的最大子段和与a[n/2+1..n]的最大子段和相同。
- a[n]的最大子段和是(a[i]+a[i+1]+…+a[j]),且1<=i<=n/2, n/2+1<=j<=n。
时间复杂度:O(n*logn)
三、动态规划法:
b[j]=max{(a[i]+a[i+1]+…+a[j])}, ( 1<=j<=n)。有b[j]定义知,当b[j-1]>0, b[j]= b[j-1]+a[j],否则b[j]=a[j]。即:
b[j]= max{ b[j-1]+a[j],a[j]}
时间复杂度:O(n)
代码:
一、蛮力算法
int MaxSum(int *a, int n, int &besti, int &bestj){
int sum = 0;
for(int i=1; i<=n; i++){
for(int j=i; j<=n; j++){
int thisSum = 0;
for(int k=i; k<=j; k++){
thisSum += a[k];
}
if(thisSum>sum){
sum = thisSum;
besti = i;
bestj = j;
}
}
}
return sum;
}
改进后:
int MaxSum(int *a, int n, int &besti, int &bestj){
int sum = 0;
for(int i=1; i<=n; i++){
int thisSum = 0;
for(int j=i; j<=n; j++){
thisSum += a[j];
if(thisSum>sum){
sum = thisSum;
besti = i;
bestj = j;
}
}
}
return sum;
}
二、分治法
int MaxSum(int *a, int left, int right){
int sum = 0;
if(left == right) {
sum = a[left]>0? a[left]:0;
}else{
int center = (left+right)/2;
int leftSum = MaxSum(a,left,center);
int rightSum = MaxSum(a, center+1,right);
int s1 = 0;
int lefts = 0;
for(int i=center;i>=left;--i){
lefts += a[i];
if(lefts>s1) s1 = lefts;
}
int s2 = 0;
int rights = 0;
for(int i=center+1;i<=right;++i){
rights += a[i];
if(rights>s2) s2 = rights;
}
sum = s1+s2;
if(sum<leftSum) sum = leftSum;
if(sum<rightSum) sum = rightSum;
}
return sum;
}
三、动态规划
int MaxSum(int *a, int n){
int sum = 0, b = 0;
for(int i = 1; i <= n; i++){
if(b>0) b += a[i];
else b = a[i];
if(b>sum) sum = b;
}
return sum;
}
测试用例:
int a[] = {0,-1,-2l,23,-4,-5,10}
测试结果: