【题目描述】
对于给定的整数序列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}
我们的目标就是求出d(A)d(A)。
【输入】
第一行是一个整数T(≤30)T(≤30),代表一共有多少组数据。
接下来是TT组数据。
每组数据的第一行是一个整数,代表数据个数据n(2≤n≤50000)n(2≤n≤50000) ,第二行是nn个整数a1,a2,…,an(|ai|≤10000)a1,a2,…,an(|ai|≤10000)。
【输出】
输出一个整数,就是d(A)d(A)的值。
【输入样例】
1
10
1 -1 2 2 3 -3 4 -4 5 -5
【输出样例】
13
【提示】
就是求最大子段和问题,样列取2,2,3,−3,42,2,3,−3,4和55,Baidu搜POJ 2479 Maximum sum,可获得大量经典最大子段和问题的题目解析,本题O(n2)O(n2)算法超时,必须用O(n)O(n)算法。
此题求解的是 两个不重合连续子段的和的最大值,所以要设置left,right数组,一个从前往后,一个从后往前,才可以保证两个序列不重合。注释在代码中。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int t,n,a[50005],i;
int left[50005],leftmax[50005];
int right[50005],rightmax[50005];
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
//初始化
left[1]=a[1];
leftmax[1]=a[1];
right[n]=a[n];
rightmax[n]=a[n];
for(i=2;i<=n;i++)//从前往后的序列中,left[i]代表以i结尾的最大序列和
left[i]=max(a[i],left[i-1]+a[i]);
for(i=n-1;i>=1;i--)//从后往前的序列中,right[i]代表以i结尾的最大序列和
right[i]=max(a[i],right[i+1]+a[i]);
for(i=2;i<=n;i++)//从前往后,leftmax[i]代表当前最大序列和(要么是前面的最大,要么是以i结尾的最大序列和left[i]最大)
leftmax[i]=max(left[i],leftmax[i-1]);
for(i=n-1;i>=1;i--)//从后往前,rightmax[i]代表当前最大序列和(要么是后面的最大,要么是以i结尾的最大序列和right[i]最大)
rightmax[i]=max(right[i],rightmax[i+1]);
int maxx=a[1];
for(i=2;i<=n;i++)//leftmax[i-1]代表1->i-1个数字序列的最大值,rightmax[i]代表i->n个数字序列的最大值,这样避免序列重复
{
maxx=max(maxx,leftmax[i-1]+rightmax[i]);
}
printf("%d\n",maxx);
}
}