环状两段最大子段和

Step 1

首先考虑链状的情况,也就是链状

++++++++-------------++++++++

P.S : ++ 表示使用的子段

可以考虑用中途相遇法,用 g[i] 表示以 ii 为结尾分界线,之前最大子段和,

以 h[i] 表示以 ii 为结尾分界线,之后最大子段和

那么答案也就是 g[i]+h[i+1] (不相交,所以要 +1 )

Step 2

如果是环状的呢?

+++---++++++----+++++++++++++++++

俗话说得好正难则反,虽然不知道三段的怎么处理,但是可以通过不取的那两段,用总和去减,就可以得到答案

注意的一个坑点

  1. 如果我们头尾都不取,只剩下 11 个,显然是不符合条件的,所以要特判存在 11 的情况

因为有负权值的存在,所以设极值的时候不能设为0,要改为-inf

Step 3

#include<bits/stdc++.h>
using namespace std;
long long n,a[200005],f[200005],g[200005],b[200005],sum;
bool check(){//特判
    int cnt=0;
    for(int i=1;i<=n;i++)
     if(a[i]>0)++cnt;
    return cnt!=1;
}
int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
     scanf("%lld",&a[i]),sum+=a[i];
    f[0]=g[0]=-0x7fffffff;//边界
    for(int i=1;i<=n;i++){//往前继承
        f[i]=max(f[i-1]+a[i],a[i]);
        g[i]=max(g[i-1],f[i]);
    }
    f[n+1]=b[n+1]=-0x7fffffff;//往后继承
    for(int i=n;i;i--){
        f[i]=max(f[i+1]+a[i],a[i]);
        b[i]=max(b[i+1],f[i]);
    }
    long long ans=-0x7fffffff;//统计第一种情况
    for(int i=1;i<=n;i++)ans=max(ans,b[i+1]+g[i]); 
    if(!check())return cout<<ans<<endl,0;//特判
    f[0]=g[0]=0x7fffffff;
    for(int i=1;i<=n;i++){//往前
        f[i]=min(a[i],f[i-1]+a[i]);
        g[i]=min(g[i-1],f[i]);
    }
    b[n+1]=f[n+1]=0x7fffffff;
    for(int i=n;i;i--){//往后
        f[i]=min(f[i+1]+a[i],a[i]);
        b[i]=min(b[i+1],f[i]);
    }
    long long ans2=0x7fffffff;//第二种答案,求出最小的再用总和去减
    for(int i=1;i<=n;i++)ans2=min(ans2,b[i+1]+g[i]); 
    if(sum-ans2==0)ans2=-0x7fffffff;//如果得到的是总和就不合题意
    else ans2=sum-ans2;//否则得到最小值
    cout<<max(ans,ans2)<<endl;//输出最大值
    return 0;
}

 

转载于:https://www.cnblogs.com/coder-cjh/p/11622676.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值