题很简单,但是方法值得保留
题目描述
给出一段序列,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个正整数N,表示了序列的长度。
第二行包含N个绝对值不大于1000000的整数,描述这段序列。
输出格式
连续最大和
法一(最朴素的算法)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
const int Minn=-0x3f3f3f;
int data[N],sum[N],bigsum[N];
int main()
{
int n=0,minn=0,mixx=Minn;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>data[i];
sum[i]=sum[i-1]+data[i];
}
minn=min(sum[1],minn);
bigsum[1]=data[1];
for(int i=2;i<=n;i++)
{
bigsum[i]=sum[i]-minn;
minn=min(sum[i],minn);
}
for(int i=1;i<=n;i++)
mixx=max(mixx,bigsum[i]);
cout<<mixx;
return 0;
法二(滚动数组,值得保留)
如果前缀和sum变成了负数,那么下一个数就不需要前面的数了(因为还不如只选它一个),这时把sum置为0,再继续累加。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
const int Minn=-0x3f3f3f;
int f[2],sum;
int main()
{
int n;
cin>>n;
cin>>f[1];
sum=f[1];
for(int i=2;i<=n;i++)
{
if(sum<0)sum=0;
scanf("%d",&f[i%2]);
sum+=f[i%2];
f[i%2]=max(f[(i-1)%2],sum);
}
cout<<f[n%2];
return 0;
}
法三(分治)
首先,假定有区间[l,r],其中间位置为mid,其最大子段为[i,j]。那么显然,i和j必定符合下列三种情况之一:
1.l ≤ i ≤ j ≤ mid
2.i ≤ mid < j ≤ r
3.mid < i ≤ j ≤ r
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
const int Minn=-0x3f3f3f;
int data[N];
int SUM(int l,int r)
{
if(l==r)
return data[l];
int mid=(l+r)>>1;
int sum=0,suml=Minn,sumr=Minn;
for(int i=mid;i>=l;i--)
{
sum+=data[i];
suml=max(sum,suml);
}
sum=0;
for(int i=mid+1;i<=r;i++)
{
sum+=data[i];
sumr=max(sum,sumr);
}
return max(max(SUM(l,mid),SUM(mid+1,r)),suml+sumr);
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&data[i]);
cout<<SUM(1,n);
return 0;
}