此题是一个最大的子段和的问题。
Given a set of n integers: A={a1, a2,..., an}, we define a function d(A) as below:
t1 t2
d(A) = max{ ∑ai + ∑aj | 1 <= s1 <= t1 < s2 <= t2 <= n }
i=s1 j=s2
Your task is to calculate d(A).
这道题用最原始的方法就是硬搜,当然我们也必然知道硬搜的方法必然是要悲剧的。因为这样要产生非常多的时间复杂度。比如枚举中间的元素,然后分别搜索出以这个元素向左以及向右的分问题的字段和,找出这些中间元素中的最大值即可。但是以中间元素为n/2为例,搜左面与搜右面差不多的话,就是n²的量级了,然而这道题能给到5万,然后就悲剧了……
这道题目交单的方法就是动态规划,可以减到o(n).具体方法如下:
首先记录从左边开始的每一个元素为中点的最长上升序列的值,注意这要计算两次。首先把小于0的点断开,然后第二次计算时,就可以计算出以一个点为右上界的最长子段的长度。右边类似。最后计算怎样加能到最长即可。这样是0(cn)。
#include<iostream>
#include<stdlib.h>
using namespace std;
int compare(const void *x,const void *y)
{
return *(int *)x-*(int *)y;//从小到大排序
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
//cin.get();
int n;
scanf("%d",&n);
int a[50002]={};
int left[50002]={};
int right[50002]={};
int i;
int num=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]>=0)num++;
}
bool ind=false;
if(num<2)
{
/*qsort(a+1,50001,sizeof(a[0]),compare);
for(i=1;i<=n;i++)
if(a[i]>=0)
{
int res=a[i]+a[i+1];
printf("%d\n",res);
ind=true;
break;
*/
ind = true;
int temp=0;
for(i=1;i<=n;i++)
for(int j=1;j<n-i;j++)
if(a[j]<a[j+1])
{temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
cout<<a[1]+a[2]<<endl;
}
if(!ind)
{
int suml=0;
for(i=1;i<=n;i++)
{
if(left[i-1]>0)
left[i]=left[i-1]+a[i];
else left[i]=a[i];
}
for(i=1;i<=n;i++)
{
if(left[i]>suml)
suml=left[i];
else left[i]=suml;
}
right[n+1]=0;
int sumr=0;
for(i=n;i>=1;i--)
{
if(right[i+1]>0)
right[i]=right[i+1]+a[i];
else right[i]=a[i];
}
for(i=n;i>=1;i--)
{
if(right[i]>sumr)
sumr=right[i];
else right[i]=sumr;
}
int max=-1000000;
for(i=1;i<n;i++)
{
if(left[i]+right[i+1]>max)
max=left[i-1]+right[i];
}
cout<<max<<endl;
}
}
return 0;
}