• 51NOD 1053:最大M子段和 V2——题解


    https://www.51nod.com/Challenge/Problem.html#problemId=1053

    N个整数组成的序列a1,a2,a3,…,an,将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。
    例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。

    WQS二分/带权二分裸题,显然函数为凸函数,问题只需要解决没有限制的情况下如何求最大值。

    f[i]为到i的最大值,则不取i就是f[i]=f[i-1],取i为f[i]=max{f[k]+sum[i]-sum[k]}-分一段代价。

    显然max{f[k]-sum[k]}可以用dp求。

    细节还是蛮多的,毕竟你还得记录求出最优值时分段个数,我的做法是限制在可能的情况下取更少的分段数,这样的情况下如果还>m就肯定不符合条件。

    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef long double dl;
    const int N=50005;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    int n,m,pcnt,g[N],maxfi[N];
    ll s[N],f[N],maxf[N],maxans;
    bool pan(ll c){
        for(int i=1;i<=n;i++){
            if(f[i-1]<maxf[i-1]+s[i]-c){
                f[i]=maxf[i-1]+s[i]-c;
                g[i]=g[maxfi[i-1]]+1;
            }else{
                f[i]=f[i-1];
                g[i]=g[i-1];
            }
            if(maxf[i-1]<f[i]-s[i]){
                maxf[i]=f[i]-s[i];
                maxfi[i]=i;
            }
            else{
                maxf[i]=maxf[i-1];
                maxfi[i]=maxfi[i-1];
                if(maxf[i-1]==f[i]-s[i]){
                    if(g[i]>g[maxfi[i-1]]) maxfi[i]=maxfi[i-1];
                    else maxfi[i]=i;
                }
            }
        }
        return g[n]<=m;
    }
    ll solve(ll l,ll r){
        ll b;
        while(l<r){
            ll mid=(l+r)>>1;
            if(!pan(mid))l=mid+1;
            else{
                b=f[n];r=mid;
            }
        }
        return l*m+b;
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=n;i++){
            ll a=read();
            if(a>0){
                ++pcnt;maxans+=a;
            }
            s[i]=s[i-1]+a;
        }
        if(pcnt<=m) printf("%lld
    ",maxans);
        else printf("%lld
    ",solve(0,1e9));
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    抚琴弹唱东流水
    借点阳光给你
    日月成双行影单
    一夜飘雪入冬来
    悼念钱学森
    我的青春谁作主
    重游望江楼有感
    雪后暖阳
    满城尽添黄金装
    敢叫岁月不冬天
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/14982210.html
Copyright © 2020-2023  润新知