题目:
Description
Your task is to calculate d(A).
Input
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, ..., an. (|ai| <= 10000).There is an empty line after each case.
Output
Sample Input
1 10 1 -1 2 2 3 -3 4 -4 5 -5
Sample Output
13
Hint
Huge input,scanf is recommended.
上一个blog已经介绍如何求一个序列的最大子段了,而这道题是要求两个子段的和最大,那么我们该怎么从求序列的最大子段,求两个子段的最大和呢?
我们假设所求两个子序列最大和为sum,两个子序列分别为left和right,这两个子段一个在序列的左边,一个在右边。那么在它们中间必定有一个分割点(这个点即可能被包含在其中一个子段中,也可能不。)设这个点为k,那么left必定是从0到k这个子段的最大子序列,right必定是从k+1到n-1(n为序列长度)这个子段的最大子序列。我们从左往右求出每个点的最大左子段和,保存在left[]数组中,然后再从右往左边求出每个点的最大右子段和,保存在right[]数组中。然后再遍历每个点,求出每个点的两个最大左右子段和,即可得出答案。
代码:
#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
int calMax(int n,int a[]){
int left[50000];//left是0到这个点的最大子序列和(包括0和这个点)
int right[50000];//right是这个点到n-1的最大子序列和(包括这个点和n-1)
int pointer1=0;
int pointer2=n-1;
int max1=-INF;
int max2=-INF;
int sum1=0;
int sum2=0;
while(pointer1<=n-1){
//以下代码为从左往右,求出每个点的右边最大子段
if(sum1>=0){
sum1+=a[pointer1];
}
else{
sum1=a[pointer1];
}
if(sum1>max1)
max1=sum1;
left[pointer1]=max1;
pointer1++;
//以下代码为从右往左,求出每个点的左边最大子段
if(sum2>=0){
sum2+=a[pointer2];
}
else{
sum2=a[pointer2];
}
if(sum2>max2)
max2=sum2;
right[pointer2]=max2;
pointer2--;
}
//遍历每个点的子段和,求出最大的那个
int max=-INF;
for(int i=0;i<=n-2;i++){
int buffer=left[i]+right[i+1];//从i点间隔,求出0到i点的最大子段和i点+1到n-1的最大子段
if(buffer>max)
max=buffer;
}
return max;
}
int main(){
int times;
scanf("%d",×);
int a[50000];
int n;
for(int i=0;i<times;i++){
scanf("%d",&n);
for(int j=0;j<n;j++){
scanf("%d",&a[j]);
}
printf("%d\n",calMax(n,a));
}
return 0;
}
我们可以注意到,题目中有一句:Huge input,scanf is recommended.
认真查了一下,发现在c++中,scanf的效率远高于cin,printf的效率远高于cout。这道题很多人就是因为用了cin才TLE,换了scanf就AC了。
看来我下次要多用scanf和printf了。。。
查阅了一些scanf的资料。
1.已知数量,且个数较少:
scanf("%d%d%d",&a&b&c);
2.已知数量,但数量较多:
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
3.不知道数量
int i=0;
while(scanf("%d",&a[i])){
i++;
}
注意如果是scanf("%d,%d,%d",&a,&b,&c);中%d,%d,%d之间有逗号,输入数据时就必须用“a逗号b逗号c逗号”这样输入。
&a,&b,&c中的&是地址运算符,&a指a在内存中的地址。scanf的作用是:按照a,b,c的内存地址将a,b,c的值存进去。
而如果scanf中%d是连着写的,如“%d%d%d”,在输入数据之间不可以加逗号,只能是空格或tab键或者回车键。
在scanf的用法中,不能出现以下情况
scanf("%.2f",&array[i]);
scanf里面不能有%.2f这样的格式,因为是键盘输入,不是计算机操作所以很难得到标准格式长度多少,有效是多少,
但是,printf可以用 %.2f 控制输出小数点的位数 (scanf("%lf",&array[i]);也是允许的~~)
在这里解释一下printf(“%6.1f\n”,s);的意思:
以浮点数据输出,6代表从小数点左边开始数,6个字符的宽度数据被保留,如果数据超出,按数据的实际长度输出,例1234567.833,输出时还是1234567.833、如果没有超出,例12345.833,会在1的前面补一个空格,如果%-6.1f,则表式从要输入的数值最左端开始数,6个字符的宽度,不足6个,则在最右端补空格;后面的.1,代表小数点后面保留的位数,例1234567.833,输出时是1234567.8 , 0.033被去掉了。\n是转义字符,输出后自动回车换行,s是要输出的变量值,整个格式为printf("格式控制符",表达式)