P1121 環狀最大兩段子段和

P1121 環狀最大兩段子段和

傳送門

思路:dp+dp+維護前後綴最值。

當兩端非跨環時,直接掃一遍premx[i]+sufmx[i+1],i[1,n)pre_{mx}[i]+suf_{mx}[i+1],i\in[1,n)即可。

當兩端跨環,即等價於求中間兩端非跨環的最小子段和,直接將a[i]a[i]取個反,就等價於求最大子段和=sum+mnsum+mn

此外此題有點細節需要注意:最小段子和不能包含整個區間,也不能將最大子段區間個數變爲1個,這樣只有一個數,不滿足兩個非空區間。

ep:n=4,{1,1,1,1}ep: n=4,\{-1,1,-1,-1\},這樣最小子段和(1),(1,1)(-1),(-1,-1)11包住了只剩一個數不能構成兩個區間。

時間複雜度:O(n)O(n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
int dp[N],pre[N],suf[N],n,sum,a[N];
int solve(){
	int ans=pre[0]=suf[n+1]=-inf;
	for(int i=1;i<=n;i++)
	 	dp[i]=max(dp[i-1]+a[i],a[i]),pre[i]=max(pre[i-1],dp[i]);
	for(int i=n;i;i--)
		dp[i]=max(dp[i+1]+a[i],a[i]),suf[i]=max(suf[i+1],dp[i]);
	for(int i=1;i<n;i++)
		ans=max(ans,pre[i]+suf[i+1]);
	return ans;
}
bool check(){
	int cnt=0;
	for(int i=1;i<=n;i++) cnt+=(a[i]>0);
	if(cnt==1) return 0;
	else return 1; 
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
	int ans=-inf;
	ans=solve();
	if(!check()) return printf("%d\n",ans),0;//特判剩餘的個數不能爲1個,不然只有一段. 
	for(int i=1;i<=n;i++ )a[i]=-a[i];
	int tmp=solve()+sum;
	if(!tmp) printf("%d\n",ans);//不能包含整個區間. 
	else printf("%d\n",max(ans,tmp));
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章