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;
}
此生无悔入OI 来生AK IOI

浙公网安备 33010602011771号