最大序列和 | ||||||
| ||||||
Description | ||||||
给出一个有n 个数的整数序列:{a1,a2,...,an},我们定义函数d(A) 如下:
你的任务是计算函数d(A)。 | ||||||
Input | ||||||
输入的第一行是一个整数T(T<=30)表示有T 组测试数据; 对于每组测试数据: 第一行输入一个整数n(2<=n<=50000); 第二行输入n 个整数:a1,a2,...,an.(|ai]<=10000)。 | ||||||
Output | ||||||
对于每组测试数据:输出一个整数表示d(A)的结果,每组输出占一行。 | ||||||
Sample Input | ||||||
1 10 1 -1 2 2 3 -3 4 -4 5 -5 | ||||||
Sample Output | ||||||
13 | ||||||
Hint | ||||||
样例中,我们选择{2,2,3,-3,4} 和{ 5 },这样我们得到答案。 | ||||||
Source | ||||||
2014.11.30新生赛-正式赛 |
思路:
1、首先考虑对于一个序列,求一段连续子序列的最大和。
那么设定dp【i】,表示以a【i】结尾的最大子段和。
不难写出其状态转移方程:dp【i】=max(dp【i-1】+a【i】,0);
2、那么接下来考虑如何求两段不相交连续子序列的最大和:
①首先我们求出来一遍正序的最大子段和dp【i】;
②接下来我们出来一遍逆序的最大子段和rdp【i】;
③接下来我们维护一个数组maxn【i】表示【1->i】区间内的最大子段和为多少。
④同理,我们再维护一个数组rmaxn【i】表示【n->i】区间内的最大子段和为多少。
那么不难想到,我们只需要O(n)维护一个output=max(maxn【i】+rmaxn【i+1】)即可。
3、注意清空需要清空的数组。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int a[56000];
int dp[56000];
int maxn[56000];
int rdp[56000];
int rmaxn[56000];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
memset(maxn,0,sizeof(maxn));
memset(rdp,0,sizeof(rdp));
memset(rmaxn,0,sizeof(rmaxn));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
dp[i]=max(dp[i-1]+a[i],0);
maxn[i]=max(maxn[i-1],dp[i]);
}
for(int i=n;i>=1;i--)
{
rdp[i]=max(rdp[i+1]+a[i],0);
rmaxn[i]=max(rmaxn[i+1],rdp[i]);
}
for(int i=1;i<=n;i++)
{
printf("%d ",dp[i]);
}
printf("\n");
int output=0;
for(int i=1;i<=n;i++)
{
output=max(maxn[i]+rmaxn[i+1],output);
}
printf("%d\n",output);
}
}