P1121

环状最大两段子段和

题目描述

给出一段长度为 \(n\) 的环状序列 \(a\),即认为 \(a_1\)\(a_n\) 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。

输入格式

第一行是一个整数 \(n\),表示序列的长度。

第二行有 \(n\) 个整数,描述序列 \(a\),第 \(i\) 个数字表示 \(a_i\)

输出格式

一行一个整数,为最大的两段子段和是多少。

样例 #1

样例输入 #1

7
2 -4 3 -1 2 -4 3

样例输出 #1

9

提示

数据规模与约定

对于全部的测试点,保证 \(2 \leq n \leq 2 \times 10^5\)\(-10^4 \leq a_i \leq 10^4\)

最大的双子段和有两种情况:
不跨越端点
有一个跨越端点
第一种可以f[i] g[i]分别维护以i结尾的 以i开头的max
第二种可以转换为sum-最小的双子段和
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[200005];
int f[200005],g[200005];
int get_min()
{
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g));
	for(int i=1;i<=n;i++)f[i]=min(f[i-1]+a[i],a[i]);
	for(int i=n;i>=1;i--)g[i]=min(g[i+1]+a[i],a[i]);
	for(int i=1;i<=n;i++)f[i]=min(f[i],f[i-1]);
	for(int i=n;i>=1;i--)g[i]=min(g[i],g[i+1]);
	int minn=INT_MAX;
	for(int i=1;i+1<=n;i++)minn=min(minn,f[i]+g[i+1]);
	return minn;
}
int get_max()
{
	memset(f,-0x3f,sizeof(f));
	memset(g,-0x3f,sizeof(g));
	for(int i=1;i<=n;i++)f[i]=max(f[i-1]+a[i],a[i]);
	for(int i=n;i>=1;i--)g[i]=max(g[i+1]+a[i],a[i]);
	for(int i=1;i<=n;i++)f[i]=max(f[i],f[i-1]);
	for(int i=n;i>=1;i--)g[i]=max(g[i],g[i+1]);
	int maxx=-INT_MAX;
	for(int i=1;i+1<=n;i++)maxx=max(maxx,f[i]+g[i+1]);
	return maxx;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	int sum=0;
	for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i];
	int ans=-INT_MAX;
	ans=max(ans,get_max());
	ans=max(ans,sum-get_min());
	cout<<ans<<"\n";
	return 0;
}
posted @ 2023-01-28 15:52  PKU_IMCOMING  阅读(39)  评论(0)    收藏  举报