洛谷 P1121 环状最大两段子段和

版权声明:本文为博主原创文章,转发请注明地址 https://blog.csdn.net/chai_jing/article/details/53024723

题目描述

给出一段环状序列,即认为A[1]和A[N]是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
输入输出格式
输入格式:

输入文件maxsum2.in的第一行是一个正整数N,表示了序列的长度。

第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列,第一个数和第N个数是相邻的。

输出格式:

输入文件maxsum2.out仅包括1个整数,为最大的两段子段和是多少。

输入输出样例
输入样例#1:

7
2 -4 3 -1 2 -4 3

输出样例#1:

9

说明

【样例说明】

一段为3


【分析】
有趣的dp题
最大子段和无非就有两种情况.
(1)跨区间的.
(2)在[1,n]中的.
然后难搞的可能是(1).
然后我们换个思路.
我们在[1,n]中求一个最小前缀/后缀和.
然后用sum减去即可.
正确性是显然的.
因为求最小的时候我们默认包括[i,i+1].
这段不选的最小子段区间必定是连续的.
故选的必定为1段(如果选的是[1,i],[i+1,n]这一段
我们也可以认为它们是分开选的两段)


【代码】

//环状最大子段和 
#include<cstdio>
#include<iostream>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=200005;
int maxl[mxn],minl[mxn],maxr[mxn],minr[mxn],a[mxn];
int main()
{
    int i,j,n,mx,mn,sum=0,ans=-1e9;
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i]),sum+=a[i];
    mx=mn=maxl[1]=minl[1]=a[1];
    fo(i,2,n)
    {
        mx=max(mx+a[i],a[i]);
        mn=min(mn+a[i],a[i]);
        maxl[i]=max(maxl[i-1],mx);
        minl[i]=min(minl[i-1],mn);
    }
    mx=mn=maxr[n]=minr[n]=a[n];
    for(i=n-1;i;i--)
    {
        mx=max(mx+a[i],a[i]);
        mn=min(mn+a[i],a[i]);
        maxr[i]=max(maxr[i+1],mx);
        minr[i]=min(minr[i+1],mn);
    }
    fo(i,1,n-1)
    {
        ans=max(ans,maxl[i]+maxr[i+1]);
        if(sum-minl[i]-minr[i+1]) ans=max(ans,sum-minl[i]-minr[i+1]);
    }
    printf("%d\n",ans);
    return 0;
}
//7
//2 -4 3 -1 2 -4 3
展开阅读全文

没有更多推荐了,返回首页