51nod 1254 最大子段和 V2
原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1254
去年做的一个题目。。。那个时候真是抓耳挠腮啊(其实刚接触ACM 也就一年多)
嘻嘻.而且我自己写的我都觉得不好。所以我想再复习一遍。。。
题目有几个点需要注意。首先必须交换。
答案的形式只可能就有三种
交换的两个元素都在答案所在段落内。
或者:
交换的元素都不在答案所在的段落内。
上面两个情况相当于没有交换。
特别的:
第三种就是。把答案所在段落的某个元素与段落外的某个元素交换。
同时。强制交换(必须交换)和 不强制交换 的答案是一样的。
注意:这是因为序列长度不可能小于2.所以我们可以随意浪费一次交换。
根据上面的分析有:
只处理第三种情况和不交换的情况取最大值即为答案。
<script type="math/tex; mode=display" id="MathJax-Element-1"></script>
对于第三种情况。我们把交换看作两种形式。
记:对于第
i
个元素 A[i] ,用
A[i]
与之前某个元素
A[k]
交换后。得到新的
A[i]
.以这个
A[i]
结尾的最大子段和为:
G[i]
为了让
G
的定义更有针对性。(只针对第三种情况)我们尽可能避免掺合不交换的情况。
对于交换后的数列:
A[1],A[2],...,A[k−1],A[i],A[k+1],...A[i−1],A[k]....
A[i]
与
A[k]
交换后,以新的A[i]结尾的。长度大于1的子段和的最大值。
maxk=1i−2(A[k]+maxt=k+1i−1(∑j=ti−1A[j]))
长度等于1的:
M[i−1]
此时 M[i]=max(A[1],A[2],...A[i])
G[i]=max(maxk=1i−2(A[k]+maxt=k+1i−1(∑j=ti−1A[j])),M[i−1])
其中。 M[i]=max(A[1],A[2],A[3],...A[i])
那么
G[i+1]=max(maxk=1i−1(A[k]+maxt=k+1i(∑j=tiA[j])),M[i])=max(G[i]+A[i],M[i])
其中
G[1]=0
当然这只是处理了一个交换方向。还有从前向后的交换。反向来一次。
G
的最大值不是答案。我们需要把G 加上另一侧的贡献的最大值才是答案。
#include <stdio.h>
#include <algorithm>
#define MAXN 50005
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
int A[MAXN];
LL Sl[MAXN];
LL Sr[MAXN];
int main ()
{
int n,i,j;
LL ans=0;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",A+i);
for(i=1,j=n;j;i++,j--)
{
Sl[i]=Sl[i-1]+A[i];
Sr[j]=Sr[j+1]+A[j];
if(Sl[i]<0)Sl[i]=0;
if(Sr[j]<0)Sr[j]=0;
if(ans<Sl[i])ans=Sl[i];
}
LL maxn=A[1],G=0;
for(int i=2;i<=n;i++)
{
G+=A[i-1];
if(G<maxn)G=maxn;
if(ans<G+Sr[i+1])ans=G+Sr[i+1];
if(maxn<A[i])maxn=A[i];
}
G=0;
maxn=A[n];
for(int j=n-1;j>0;j--)
{
G+=A[j+1];
if(G<maxn)G=maxn;
if(ans<G+Sl[j-1])ans=G+Sl[j-1];
if(maxn<A[j])maxn=A[j];
}
printf("%lld\n",ans);
return 0;
}