求最大连续段和
给出一串长度为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;
}