T1305:Maximum sum

【题目描述】
对于给定的整数序列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);
	}
}

猜你喜欢

转载自blog.csdn.net/lemon1090/article/details/89435157
今日推荐