码迷,mamicode.com
首页 > 其他好文 > 详细

最大子段和(交换)

时间:2020-06-27 20:05:15      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:namespace   fine   turn   nbsp   一起   ret   int   交换   color   

题意:

  给你一个序列找到交换一次后的最大字段和(所有交换方式中最大的)

分析:

  首先我们这么想,我们要最后要的答案是由某个区间和区间左边交换一个或和区间右边交换一个或者不交换(内部交换或外部交换)。

  至于不交换,我们直接o(n)搞过。

  然后就是向左(向右类似,只要把序列倒过来跑一边就好了)交换,我们可以得到:ans=max(sum[i]-sum[j]-a[k1]+a[k2])(i>j,j<k1<=i,k2<=j),我们只要n的4次方枚举就好了。

  可是我们怎么优化呢?

  我们加一个括号:sum[i]-(sum[j]+a[k1]-a[k2])(范围略去),然后我们就可以n的效率枚举i,然后提前预处理一下min(sum[j]+a[k1]-a[k2]),用一个数组记录一下min(枚举合法的j,k1,k2),然后求一下前缀max就好了,效率到了n的3次方,然后类似的,我们只需要n的效率枚举k1,然后n×n求sum[j]-a[k2]的最小值,效率变成了n×n,最后再n的效率枚举j,n的效率求k2的最小值,效率变成了n,最后整合到一起就好了。

  代码:

  

#include <cstdio>
#include <string>
using namespace std;
#define ll long long
const int maxn=5e4+10;
const ll inf=1e18;

ll a[maxn];
ll sum[maxn];
ll b[maxn];
int main(){
    int n;
    scanf("%d",&n);
    ll mi=0ll,ans=-inf,nowj=0ll,ma=-inf;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
        ans=max(ans,sum[i]-nowj);
        nowj=min(nowj,a[i]+mi);
        ma=max(ma,a[i]);
        mi=min(sum[i]-ma,mi);
    }
    mi=0ll;
    for(int i=1;i<=n;i++){
        ans=max(ans,sum[i]-mi);
        mi=min(sum[i],mi);
    }
    for(int i=1;i<=n;i++)
        b[i]=a[n-i+1];
    for(int i=1;i<=n;i++)
        a[i]=b[i];
    mi=0ll,nowj=0ll,ma=-inf;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
        ans=max(ans,sum[i]-nowj);
        nowj=min(nowj,a[i]+mi);
        ma=max(ma,a[i]);
        mi=min(sum[i]-ma,mi);
    }
    printf("%lld",ans);
    return 0;
}

 

  

最大子段和(交换)

标签:namespace   fine   turn   nbsp   一起   ret   int   交换   color   

原文地址:https://www.cnblogs.com/wish-all-ac/p/13112002.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!