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);
}
posted @ 2021-04-14 19:25  TRTTG  阅读(96)  评论(0编辑  收藏  举报