51nod 1254 最大子段和 V2(递推)

http://www.51nod.com/Challenge/Problem.html#problemId=1254

最终答案只有2种情况
1、不交换最大,相当于随便交换2个在最大子段和里的数。
2、把一个原本不在最大子段和里的数换到最大子段和里。

第1种情况就跑一遍求最大子段和就好
对于第2种情况:
假设现在要交换第i个和第k个 (k < i),并且第k个是我们想要的,第i个是要换走的。
定义(g[i])表示把(a[i])(a[k])交换之后,以新的第i个数为最后一个数字的最大子段和
定义(r[i])表示以i为第一个数字的最大子段和
那么在交换的2个数是换走后一个、选用前一个的前提下,最大子段和可以表示为(max(g[i]+r[i+1]))
对于 i > k 的情况,也就是交换的2个数是换走前一个、选用后一个,把a数组逆序再做一遍即可。

如何快速求出(g[i]) ?
(g[i]=max_{k=1}^{i-2}(a[k] + max_{h=k+1}^{i-1}(sum_{j=h}^{i-1}a[j]))), 子段长度 (leq) 2
(g[i]=max_{j=1}^{i-1} a[j]),子段长度 (=1)
所以
(g[i+1]=max(max_{k=1}^{i-1}(a[k]+max_{h=k+1}^i(sum_{j=h}^ia[j])),max_{j=1}^i a[j])=max(g[i]+a[i],max_{j=1}^i a[j]))

#include<cstdio>
#include<algorithm>

using namespace std;

#define N 500003

int n,a[N];
long long r[N];
long long ans=-1e17;

void solve()
{
	for(int i=n;i;--i) r[i]=max(0ll,r[i+1]+a[i]);
	int m=a[1];
	long long g=a[1];
	for(int i=2;i<=n;++i) 
	{
		ans=max(ans,g+r[i+1]);
		m=max(m,a[i]);
		g=max(g+a[i],1ll*m);
	}
}

int main()
{
	scanf("%d",&n);
	long long now=0;
	for(int i=1;i<=n;++i) 
	{
		scanf("%d",&a[i]);
		now+=a[i];
		if(now<0) now=0;
		ans=max(ans,now);
	}
	solve();
	int t=n/2;
	for(int i=1;i<=t;++i) swap(a[i],a[n-i+1]);
	solve();
	printf("%lld",ans);
}
作者:xxy
本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/14659526.html