P1121 環狀最大兩段子段和
思路:維護前後綴最值。
當兩端非跨環時,直接掃一遍即可。
當兩端跨環,即等價於求中間兩端非跨環的最小子段和,直接將取個反,就等價於求最大子段和=。
此外此題有點細節需要注意:最小段子和不能包含整個區間,也不能將最大子段區間個數變爲1個,這樣只有一個數,不滿足兩個非空區間。
,這樣最小子段和把包住了只剩一個數不能構成兩個區間。
時間複雜度:
#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;
}