【基础】dp系列1

序列双段最大子段和问题

(也许很水但蒟蒻刚刚学dp就来记录一下)

题目链接

题意就是求序列中的任意两段的最大子段和最大。

我们先预处理出来前缀和,方便求最大子段和。

对于每一个i都求一遍1到i的最大子段和,即:

pre[1]=a[1];
    minn=min(0,a[1]);
    for(int i=2;i<n;i++){
        minn=min(minn,a[i]);
        pre[i]=max(pre[i-1],a[i]-minn);
    }

其中,a数组是前缀和,直接处理的。

那么,我们再对n到1的每一个i求一遍最大子段和,即:

post[n]=a[n]-a[n-1];
    maxn=a[n];
    for(int i=n-1;i>1;i--){
        maxn=max(maxn,a[i]);
        post[i]=max(post[i+1],maxn-a[i-1]);
    }

那么,接下来我们要做些什么?

枚举每一个i,从1到n-1,求出前i个数的最大子段和和i第i+1个数到n的最大子段和之和的最大值。

即:

ans=-inf;
    for(int i=1;i<n;i++)ans=max(ans,pre[i]+post[i+1]);
    printf("%d\n",ans);

完整代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define inf 2147483647
using namespace std;
int t,n,a[50001];
int maxn,minn,pre[50001];
int post[50001],ans;
inline void solve(){
    pre[1]=a[1];
    minn=min(0,a[1]);
    for(int i=2;i<n;i++){
        minn=min(minn,a[i]);
        pre[i]=max(pre[i-1],a[i]-minn);
    }
    post[n]=a[n]-a[n-1];
    maxn=a[n];
    for(int i=n-1;i>1;i--){
        maxn=max(maxn,a[i]);
        post[i]=max(post[i+1],maxn-a[i-1]);
    }ans=-inf;
    for(int i=1;i<n;i++)ans=max(ans,pre[i]+post[i+1]);
    printf("%d\n",ans);
    memset(pre,0,sizeof(pre));
    memset(post,0,sizeof(post));
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",&a[i]),a[i]+=a[i-1];
        solve();
    }
    return 0;
}

 

posted @ 2019-07-05 09:36  Refined_heart  阅读(181)  评论(0编辑  收藏  举报