您的位置:首页 > 其它

【算法设计】最大子段和问题

2012-03-29 23:16 302 查看
(一) 问题描述

输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值,并给出子段起始地址。

(二) 算法设计

2.1简单算法

看到这道题最简单的算法即依次计算从1到n内所有可能子段和,以sum记录最优值。代码如下:

int maxSum_sim(int a[],int n){
int sum=0,this_sum=0;
int besti,bestj;
for(int i=0;i<n;i++)
for(int j=i;j<n;j++){
this_sum=0;
for(int k=i;k<=j;k++)
this_sum+=a[k];
if(this_sum>sum){
sum=this_sum;
besti=i;
bestj=j;
}
}
printf("%d %d\n",besti,bestj);
return sum;

}


复杂度是O(n3)

2.2 优化

容易看出,2.1中代码出现大量的重复计算,即在第三个for里,由于

,因此大可以利用变量this_sum记录上一个上一个停止下标(同一个开始下标i)的求和。代码如下:

int maxSum_sim2(int a[],int n){
int sum=0,this_sum=0;
int besti=0,bestj=0;
for(int i=0;i<n;i++){
this_sum=0;
for(int j=i;j<n;j++){
this_sum+=a[j];
if(this_sum>sum) {
sum=this_sum;
besti=i;
bestj=j;
}
}
}
printf("%d %d\n",besti,bestj);
return sum;
}


复杂度为O(n2)

2.3 动态规划

求1到j里的最大段和问题可将问题分为:求1到j-1的最大子段和与含有了j的最大子段和的比较问题,表达式如下

C[j]=max(C[j-1],t[j])

其中C[j]表示1到j的最大子段和,t[j]表示以j结束的最大子段和。代码如下:

int maxSum_DP(int a[],int n){
int sum=0,this_sum=0;
int besti=0,bestj=0;
int i=0;
for(int j=0;j<n;j++){
this_sum+=a[j];
if(this_sum>sum){
sum=this_sum;
besti=i;
bestj=j;
}
else if(this_sum<=0){
i=j+1;
this_sum=0;
}
}
printf("%d-->%d\n",besti,bestj);
return sum;
}

利用sum记录已有的最优值即C[j-1],利用this_sum计算加上a[j]后的值。若this_sum大于sum,则更新sum,并继续该过程;若this_sum小于等于0,则可将this_sum重新置于0,更新其实坐标为j+1,开始新一轮的探试累加(详见后文);若this_sum仅仅只是小于sum,则可继续累加。

当this_sum小于等于0时之所以可以开始新一轮的子段累加:

假设最终子段∑中包括了该子段∑1,剩下的子段是∑2,即
∑=∑1+∑2,且∑=maxValue
∵ ∑1<=0
∴∑-∑2<=0,即∑2>=∑。
这与∑=maxValue矛盾,所以假设不成立。
∴在最终的∑中必定不包含∑1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: