题目描述
给出一段环状序列,即认为A[1]和A[N]是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
输入输出格式
输入格式:
输入文件maxsum2.in的第一行是一个正整数N,表示了序列的长度。
第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列,第一个数和第N个数是相邻的。
输出格式:
输入文件maxsum2.out仅包括1个整数,为最大的两段子段和是多少。
输入输出样例
输入样例#1:
7
2 -4 3 -1 2 -4 3
输出样例#1:
9
说明
【样例说明】
一段为3
【分析】
有趣的dp题
最大子段和无非就有两种情况.
(1)跨区间的.
(2)在[1,n]中的.
然后难搞的可能是(1).
然后我们换个思路.
我们在[1,n]中求一个最小前缀/后缀和.
然后用sum减去即可.
正确性是显然的.
因为求最小的时候我们默认包括[i,i+1].
这段不选的最小子段区间必定是连续的.
故选的必定为1段(如果选的是[1,i],[i+1,n]这一段
我们也可以认为它们是分开选的两段)
【代码】
//环状最大子段和
#include<cstdio>
#include<iostream>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=200005;
int maxl[mxn],minl[mxn],maxr[mxn],minr[mxn],a[mxn];
int main()
{
int i,j,n,mx,mn,sum=0,ans=-1e9;
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]),sum+=a[i];
mx=mn=maxl[1]=minl[1]=a[1];
fo(i,2,n)
{
mx=max(mx+a[i],a[i]);
mn=min(mn+a[i],a[i]);
maxl[i]=max(maxl[i-1],mx);
minl[i]=min(minl[i-1],mn);
}
mx=mn=maxr[n]=minr[n]=a[n];
for(i=n-1;i;i--)
{
mx=max(mx+a[i],a[i]);
mn=min(mn+a[i],a[i]);
maxr[i]=max(maxr[i+1],mx);
minr[i]=min(minr[i+1],mn);
}
fo(i,1,n-1)
{
ans=max(ans,maxl[i]+maxr[i+1]);
if(sum-minl[i]-minr[i+1]) ans=max(ans,sum-minl[i]-minr[i+1]);
}
printf("%d\n",ans);
return 0;
}
//7
//2 -4 3 -1 2 -4 3