1051. 最大的和
1.题目
对于给定的整数序列 A={a1,a2,…,an}A={a1,a2,…,an},找出两个不重合连续子段,使得两子段中所有数字的和最大。
我们如下定义函数 d(A)d(A):
d(A)=max1≤s1≤t1<s2≤t2≤n{∑i
=s1t1ai+∑j
=s2t2aj}d(A)
=max1≤s1≤t1<s2≤t2≤n{∑i
=s1t1ai+∑j
=s2t2aj
(1)输入格式
第一行是一个整数 TT,代表一共有多少组数据。
接下来是 TT 组数据。
每组数据的第一行是一个整数,代表数据个数据 nn,第二行是 nn 个整数 a1,a2,…,an,a1,a2,…,an。
(2)输出格式
每组数据输出一个整数,占一行,就是 d(A)d(A) 的值。
(3)数据范围
1≤T≤301≤T≤30, 2≤n≤500002≤n≤50000, |ai|≤10000|ai|≤10000
(4)输入样例
1 10 1 -1 2 2 3 -3 4 -4 5 -5
(5)输出样例:
13
2.解析
前缀和与后缀和分离。
代码的关键部分在于part函数,分别用g[i]和f[i]表示i以前最长连续数组的和与i以后最长连续数组的和,我们要求的是g[i]与f[i]之和的最大值。当i取不同值时(即i在不同位置插入时),g[i]与f[i]之和的值不同。所以我们要做的是,使i从1到n遍历,(改变插入i的位置),获得二者之和最大的地方。
在求g[i]与f[i]时,s是开路将军,g[i]和f[i]跟着s走。
走法分为两步
1.若前缀和小于零则舍弃前面的所有数,体现在s=max(s,0);
(s会舍弃前面不需要的数)
2.若在某个数后出现了一到多个负数,f[i]就会先不动,由s去探路,若一直到n-1,能出现足够的 大的正数能使得加入前面所有负数后的s值能大于f[i],则f[i]跳到s所在的位置
3.代码
#include<stdio.h>
#define N 5000
int g[N],f[N],w[N],a[N];
int max(int x,int y)
{
if(x<y)
return y;
else
return x;
}
int part(int w[],int n)
{
int i,s,res;
g[0]=-1;
for(i=1,s=0;i<=n;i++)
{
s=max(0,s)+w[i];
g[i]=max(g[i-1],s);//s用来替
}
f[0]=-1;
for(i=n,s=0;i>=1;i--)
{
s=max(0,s)+w[i];
f[i]=max(f[i+1],s);
}
res=-1;
for(i=1;i<=n;i++)
{
res=max(res,g[i]+f[i+1]);
}
printf("%d\n",res);
return 0;
}
int main()
{
int i,j,k,t,n;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
scanf("%d",&n);
for(j=1;j<=n;j++)
{
scanf("%d",&a[j]);
}
part(a,n);
}
return 0;
}