P1121 环状最大两段子段和

发布时间 2023-06-09 08:50:43作者: 月光幻影

P1121 环状最大两段子段和

非环状最大两子段和

\[fr[i]表示第1 \to i个元素的最大子段和 ba[i]表示第n \to i个元素的最大子段和 所以最大两子段和就是max(fr[i]+ba[i+1]),i \in [1,n) \]

int Solve(){
	int ans=-0x3f3f3f3f;
	
	for(int i=1;i<=n;i++) fr[i]=max(fr[i-1],0ll)+a[i];
	for(int i=1;i<=n;i++) fr[i]=max(fr[i],fr[i-1]);
	
	for(int i=n;i>=1;i--) ba[i]=max(ba[i+1],0ll)+a[i];
	for(int i=n;i>=1;i--) ba[i]=max(ba[i],ba[i+1]);
	
	for(int i=1;i<n;i++)
		ans=max(ans,fr[i]+ba[i+1]);
		
	return ans;
}

环状最大两子段和

  • 求非环状最大两子段和 \(ans1\),再求非环状最小两子段和 \(ans2\),这样两个最小子段和能把环分成两部分,就是环状最大两子段和\(sum+ans2(负)\),与非环状最大两子段和取最大值即可

  • 求非环状最小两子段和,其实就是求原序列的相反数的非环状最大两子段和,但是如果序列中只有一个正数会导致所求的非环状最大两子段相连,只将序列分为一部分,所以如果序列中只有一个正数,那么答案就是非环状最大两子段和‘

  • 如果序列中全为负数,那么原序列的相反数的非环状最大两子段和会等于原序列和的相反数,导致\(sum+ans2=0\),无效,所以答案就是\(ans1\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;

int n;
int a[N],fr[N],ba[N];
int sum,cnt,ans1,ans2;

int Solve(){
	int ans=-0x3f3f3f3f;
	
	for(int i=1;i<=n;i++) fr[i]=max(fr[i-1],0ll)+a[i];
	for(int i=1;i<=n;i++) fr[i]=max(fr[i],fr[i-1]);
	
	for(int i=n;i>=1;i--) ba[i]=max(ba[i+1],0ll)+a[i];
	for(int i=n;i>=1;i--) ba[i]=max(ba[i],ba[i+1]);
	
	for(int i=1;i<n;i++)
		ans=max(ans,fr[i]+ba[i+1]);
		
	return ans;
}

signed main(){
	freopen("1.in","r",stdin);
	memset(fr,-0x3f3f3f3f,sizeof(fr));
	memset(ba,-0x3f3f3f3f,sizeof(ba));
	
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
		if(a[i]>0) cnt++;
	}
	
	ans1=Solve();
	if(cnt==1){
		cout<<ans1<<"\n";
	}else{
		memset(fr,-0x3f3f3f3f,sizeof(fr));
		memset(ba,-0x3f3f3f3f,sizeof(ba));
					
		for(int i=1;i<=n;i++)
			a[i]=-a[i];
		ans2=sum+Solve();
		if(!ans2) ans2=-0x3f3f3f3f;
		cout<<max(ans1,ans2)<<"\n";
	}
}