环状最大两段子段和

传送门

题意:

简单说
就是给个序列
求和最大的两段
且序列头可接尾

解法:

若不考虑环
则可对每一个位置
分成其前一段和其后一段
求其前一段与后一段的最大最段和相加
找到最大的值

在考虑环的情况
可以把原问题转化为求中间最小的两段
再用序列全部的和 减去 最小的两段
为了偷懒
直接将序列所有数取相反数
然后按照第一种做法做

当然序列中只有一个正数和无正数时这么做就错了
因为求出来的值需要至少有一个序列为空
此时我们就并不需要做考虑环的操作
直接输出不考虑环的值就行

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#define inf 2000000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
int n,ans1,ans2,sum=0,tot=0,a[200010],f[200010],g[200010];
int solve()
{
    int ans=-inf;
    rep(i,1,n) f[i]=max(f[i-1],0)+a[i];
    dwn(i,n,1) g[i]=max(g[i+1],0)+a[i];
    rep(i,2,n) f[i]=max(f[i-1],f[i]);
    dwn(i,n-1,1) g[i]=max(g[i+1],g[i]);
    rep(i,1,n-1) ans=max(ans,f[i]+g[i+1]);
    return ans;
}
int main()
{
    scanf("%d",&n);
    rep(i,1,n)
    {
        scanf("%d",&a[i]);
        sum+=a[i],tot+=(a[i]>0);
    }
    ans1=solve();
    if(tot<=1)
    {
        printf("%d\n",ans1);
        return 0;
    }
    rep(i,1,n) a[i]=-a[i];
    ans2=sum+solve();
    printf("%d\n",max(ans1,ans2));
    return 0;
}

转载于:https://www.cnblogs.com/MYsBlogs/p/11042512.html

你可能感兴趣的:(环状最大两段子段和)