最大子列和问题

最大子列和

题目:给定N个整数的序列{ A 1 A_1 A1, A 2 A_2 A2, A 3 A_3 A3 A n A_n An},求函数f(i,j)=max{0, ∑ i j   A n \sum_i^j\ A_n ij An}的最大值。

即找出序列间连续的数字之和最大值

1.三重循环

最外层循环定义左边界i,次外层循环定义右边界j,而最内层循环则将[i,j]区间内的值相加再与最大子列和比较,若大于则赋值给最大子列和。

int MaxSubeseqSum1(int A[],int n)
{
	int thisSum = 0,maxSum = 0;
	for(int i = 0; i<n; i++){
		for(int j = i;j<n;j++){
		thisSum = 0;
		for(int k = i; k<=j; k++){
			thisSum +=A[k];
		} 
		if(thisSum > maxSum)
		maxSum = thisSum;
		} 

	} 
	return maxSum;
 }

时间复杂度为O( n 3 n^3 n3)

办法太过于暴力,有没有更好的办法呢?答案是有的。我们知道其实在最内层循环的时候不必每次都从最左值加到最右值,只需要将上一次的和在往右边加一项即可,也就是说k这趟最内层的循环可以省掉。

2.二重循环

我们只需要改一改上面的代码,将k的最内层循环省掉,计算和放在j的次外层循环。

 int MaxSubseqSum2(int A[],int n)
 {
 	int thisSum = 0,maxSum = 0;
 	for(int i =0; i<n;i++){
 		thisSum = 0;
 		for(int j = i;j<n;j++){
 			thisSum +=A[j];
		 }
		 if(thisSum > maxSum)
		 maxSum = thisSum;
	 }
	 return maxSum;
 }

时间复杂度为O( n 2 n^2 n2)

写出以上的代码已经有很大的进步,但是 n 2 n^2 n2同样是不是一个很好的算法,我们还可以采用分治的思想更进一步优化算法。

3.分治

分治,分而治之,即把大问题化成小问题。
我们来借助一组数据来说明这个问题:{6,2,-1,7,5,-4,3,2}
在这里插入图片描述我们利用递归将数组不断切成两半,我们挑左半部分的前四步来看
在这里插入图片描述初始化最大子列和为0,先将6对比最大子列和比较发现6>最大子列和,将6赋值给最大子列和,再将6+2与最大子列和比较(6<6+2=8)则将8赋值给最大子列和,然后-1和7也如此。最关键的是在6、2、-1、7之中中间界限也要加入比较即2+(-1)也要比较。直接贴代码理解吧。

//左扫描
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;
	
	 if(RightBorderSum>MaxRightBorderSum)
	 MaxRightBorderSum = RightBorderSum; 
	 }
	 return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum );
 }
 

 int MaxSubseqSum3(int A[],int n)
{
	return Div(A,0,n-1);
 } 

时间复杂度为O( n l o g n nlog_n nlogn)

使用分治我们将算法优化到了O( n l o g n nlog_n nlogn),那有没有时间复杂度为O( n n n)的算法呢,即扫描一次即可得到最大子列和的算法,答案同样是有的。

4.在线处理

我们在解决这个问题的时候,最关键的是找到连续的最大数值之和,注意是连续,所以我们可以定义一个当前数值之和与最大子列和相比较,当当前子列和小于0时我么知道前面连续的和都为不可取,所以我们将前面的子列和都扔掉(赋值0),再进行下面的计算。

  int MaxSubseqSum4(int A[],int n)
  {
  	int maxSum,thisSum,i;
  	maxSum = 0,thisSum = 0;
  	for( i=0; i<n; i++){
  		thisSum += A[i];
  		if(thisSum > maxSum){
  			maxSum = thisSum;
		  }
		  else if(thisSum<0){
		  	thisSum = 0;
		  }
	  }
  	return maxSum;
  }

时间复杂度为O( n n n)
这应该已经是最好的算法了,因为解决这个问题至少要扫描一趟这个数组。

完整代码

#include<stdio.h>

int MaxSubeseqSum1(int A[],int n)
{
	int thisSum = 0,maxSum = 0;
	for(int i = 0; i<n; i++){
		for(int j = i;j<n;j++){
		thisSum = 0;
		for(int k = i; k<=j; k++){
			thisSum +=A[k];
		} 
		if(thisSum > maxSum)
		maxSum = thisSum;
		} 

	} 
	return maxSum;
 }
 
 int MaxSubseqSum2(int A[],int n)
 {
 	int thisSum = 0,maxSum = 0;
 	for(int i =0; i<n;i++){
 		thisSum = 0;
 		for(int j = i;j<n;j++){
 			thisSum +=A[j];
		 }
		 if(thisSum > maxSum)
		 maxSum = thisSum;
	 }
	 return maxSum;
 }
 
 //分而治之
 int Max(int a,int b,int c)
 {
 	if(a<b)
 	a = b;
 	if(a<c)
 	a = c;
 	return a;
 	
 }
 int Div(int A[],int left,int right)
 {
 	int MaxLeftSum,MaxRightSum;
 	int MaxLeftBorderSum, MaxRightBorderSum;
 	int LeftBorderSum,RightBorderSum;
 	int center,i;
 	
 	if(left == right){
 		if(A[left]>0) return A[left];
 		else return 0;
	 }
	 
	 center = (left + right)/2;
	 MaxLeftSum = Div(A,left,center);
	 MaxRightSum = Div(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;
	
	 if(RightBorderSum>MaxRightBorderSum)
	 MaxRightBorderSum = RightBorderSum; 
	 }
	 return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum );
 }
 

 int MaxSubseqSum3(int A[],int n)
{
	return Div(A,0,n-1);
 } 
  //在线处理 
  int MaxSubseqSum4(int A[],int n)
  {
  	int maxSum,thisSum,i;
  	maxSum = 0,thisSum = 0;
  	for( i=0; i<n; i++){
  		thisSum += A[i];
  		if(thisSum > maxSum){
  			maxSum = thisSum;
		  }
		  else if(thisSum<0){
		  	thisSum = 0;
		  }
	  }
  	return maxSum;
  }
 int main()
 {
 	int n;
 
 	scanf("%d",&n);
 	int A[n];
 	for(int i = 0;i<n;i++)
 	scanf("%d",&A[i]);

	 	printf("%d\n",MaxSubeseqSum1(A,n));
	 		printf("%d\n",MaxSubseqSum2(A,n));
	 		printf("%d\n",MaxSubseqSum3(A,n));
	 			printf("%d\n",MaxSubseqSum4(A,n));
 	return 0;
  } 

还在学习,还在成长,如有错误望能指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值