【刷题】求最大连续段和(三种方法)

给出一串长度为n的数列,要求从中找出连续的一段来使得总和最大。输人包含两行,第1行表示数列长度为N(N<=100000),第2行包括N个整数来描述这个数列,每个整数的绝对值不超过1000。输出只有一个整数,为最大的连续段总和。

输入样例:

5
1 -2 3 1 -4

输出样例:

4

分析:
求“最大连续段和”是一个比较基础的问题,可以用多种方法解决,下面讨论三种算法。

方法一:穷举法

采用三重循环穷举子段起点位置、终点位置、求子段和,比较子段和求最大值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100001;	
int main(){
	int a[maxn];
	int n,i,j,k;
	int maxsum;
	int temp;
	cin>>n;
	for(i=1;i<=n;i++)
		cin>>a[i];
	maxsum=a[1];
	for(i=1;i<=n;i++) //子段起始位置 
	{
		for(j=i;j<=n;j++){ //子段终点位置 
			temp=0;
			for(k=i;k<=j;k++)//求子段和 
				temp=temp+a[k];
			if(maxsum<temp) maxsum=temp;
		}
	}
	cout<<maxsum; 
}

方法二:去除重复操作

方法1中,在求子段和过程中有许多重复的操作,如:求第3个数到第5个数的和包含在求第2个数到第5个数和之中,显然,重复进行了求和操作。如何解决呢?

解决方法是通过预处理,求出从第1位置到每个位置的和s[i],而s[i]值等于s[i-1]值加当前位置值,减少了重复求和运算,对于子段[i,j]的子段和则为s[j]-s[i]。

这样,用两重循环穷举子段起点位置与终点位置j,求得所有子段和,比较子段和求最大值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100001;	
int main(){
	int a[maxn],s[maxn];
	memset(s,0,sizeof(s)); 
	int n,i,j;
	int maxsum;
	int temp;
	cin>>n;
	for(i=1;i<=n;i++){
		cin>>a[i];
		s[i]=a[i]+s[i-1];//预处理前缀和 
	}
	maxsum=a[1];
	for(i=1;i<=n;i++){ //子段起始位置 
		for(j=i;j<=n;j++){ //子段终点位置 
			temp=s[j]-s[i-1]; 
			if(maxsum<temp) maxsum=temp;
		}
	}
	cout<<maxsum; 
}

方法3:贪心算法

设a[i]为以第j个位置为结尾的最大子段和,若a[i-1]大于0,显然,a[i]=a[i-1]+当前数,因为,与和大于0连续序列构成的序列和一定比本身数构成的序列和大,如果a[i-1]小于0,则a[i]=当前数,因为,当前数大于当前数加上一个负数。

这里应用了一个贪心思想,当前数+正数>当前数,当前数+负数<当前数。通过求从第1个到第n个数以本身数为结尾的最大子段和,即枚举了所有数最大子段和。

具体实现步骤如下:
(1)读入序列中的n个数存放在a数组中。
(2)初值设置,第1个数的子段和t=a[1],最大子段和ans=a[1]。
(3)一重循环枚举第2个数到第n个数,执行下列操作:
①求以当前数为结尾的最大子段和t。
②如果t>ans,刷新ans值。
(4)输出ans值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100001;	
int main(){
	int a[maxn];
	int n,i;
	int ans;//和 
	int t;//当前数 
	cin>>n;
	for(i=1;i<=n;i++){
		cin>>a[i];
	}
	ans=a[1];
	t=ans;
	for(i=2;i<=n;i++){
		if(a[i]>=0)
			t=t+a[i];
		else 
			t=0; //清零 
		if(t>ans) ans=t;
	}
	cout<<ans; 
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

relizi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值