循环数组的最大子段和即在一段数字中最大的子段和也可能是头一部分尾一部分组成的子段,就像是这串数字成了一个圆环,来求最大子段和。
N个整数组成的循环序列a11,a22,a33,…,ann,求该序列如aii+ai+1i+1+…+ajj的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑an−1n−1,ann,a11,a22这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数 (-10^9 <= Sii <= 10^9)
输出循环数组的最大子段和。
6
-2
11
-4
13
-5
-2
20
一段数字的最大子段的情况可以分为两种:
一、最大子段和不在头尾连接处 ,如图
二、最大子段和在头尾处,如图:
所以,可以分别计算这两种情况,最后比较哪个比较大,输出大的即可。第二种计算时,当我们假设最大子段和为这种情况时,我们可以知道空白的子段是最小子段和,因为那段若不是最小子段和,则还可以有数字并入最大子段中,最大子段就不是最大子段了,这与我们假设的相反,所以,我们设最大子段和为s,最小子段和为A,则 s=sum(总和)- A;所以我们只需要求最小子段和。
应注意求最小子段和时不能用一般的求最大子段和的方法求,需要先将所有的数字取负,然后求最大子段和,A= -S1。
#include
#include
#include
using namespace std;
const int maxn=50005;
__int64 a[maxn],dp1[maxn],dp2[maxn];
int main()
{
int n,e1=1,e2=1;
__int64 sum=0,da1,minn,da2;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%I64d",&a[i]);
sum+=a[i];
}
da1=a[1];
for(int i=1;i<=n;i++){
dp1[i]=max(a[i],dp1[i-1]+a[i]); //求最大子段和
if(dp1[i]>da1){
da1=dp1[i];
e1=i;
}
}
for(int i=1;i<=n;i++)
a[i]=-a[i];
da2=a[1];
for(int i=1;i<=n;i++){
dp2[i]=max(a[i],dp2[i-1]+a[i]); //取负后求最大子段和
if(dp2[i]>da2){
da2=dp2[i];
e2=i;
}
}
minn=-dp2[e2]; //求最小子段和
if(dp1[e1]>(sum-minn))
printf("%I64d\n",dp1[e1]); //比较两种情况求的子段和的大小,取大
else
printf("%I64d\n",sum-minn);
return 0;
}
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1050