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

(一)  问题描述

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


(二)  算法设计

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。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值