• Cut the Sequence


    Cut the Sequence

    有一个长度为n的序列({a_i}),现在求将其划分成若干个区间,并保证每个区间的和不超过m的情况下,每个区间的最大值的和的最小值,(0 < N ≤ 100 000)

    不难想到,设(f_i)表示把前i个位置划分后的所求,设s为a的前缀和,于是有

    [f_i=min_{j=0,s_i-s_jleq m}^{i-1}{f_j+max_{k=j+1}^i{a_k}} ]

    关键在于这是(O(n^2))递推,于是有以下结论优化

    • f具有单调性

    把得到(f_i)的方案中,去除掉第i个位置变为(f_{i-1}),要么不变,要么变小,而此时得到(f_{i-1}leq f_i),于是得证。

    • 最优决策点j必然满足其中之一
    1. (max_{k=j}^i{a_k}=a_j)(即(a_j)大于等于(a_{j+1},...,a_i)
    2. (s_i-s_{j-1}>m)(即j是最小的j使(s_i-s_jleq m))

    对于最优决策点而言,如果前面的决策点比其更优,必然有(s_i-s_{j-1}leq m),并且(f_{j-1}+max_{k=j}^i{a_k}leq f_j+max_{k=j+1}^i{a_k}),而(f)本身具有单调性,为了让结果满足条件(max_{k=j}^i{a_k}= max_{k=j+1}^i{a_k}),即(a_kleq max_{k=j+1}^i{a_k})

    于是当(s_i-s_{j-1}> m),(a_kgeq max_{k=j+1}^i{a_k})时,j可能为最优决策点。

    因此对于即j是最小的j使(s_i-s_jleq m),我们假设在第i-1个位置的j已经维护出来,而考虑第i个位置,此时多加了一个(a_i),如果不能满足条件,只要往后拉即可,这样可以做到(O(n))

    至于(a_j)大于等于(a_{j+1},...,a_i),故如果转移到求(f_{i+1}),多了一个(a_{i+1}),如果(a_{i+1}>a_j),必然它就不再满足了,于是可以去除,而(s_{i}-s_{j})又是一个单调的函数,于是我们可以维护单调递减的单调队列维护决策点a的单调性,并保证s的不超过范围。

    现在问题在于如何快速求出决策点转移过来的值,易知对于决策点j(f_j)已是确定了,因为单调队列的性质,容易知道,决策点j的(max_{k=j+1}^i{a_k})也就是j在单调队列中的位置的下一个位置的a,根据这一性质,注意对单调队列末尾的特判即可。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <set>
    #define il inline
    #define ri register
    #define ll long long
    #define Size 100100
    using namespace std;
    multiset<int>S;
    ll dp[Size];
    int a[Size],T[Size],L,R;
    template<class free>
    il void read(free&);
    template<class free>
    il free Min(free,free);
    int main(){
        int n;ll m,sum(0);read(n),read(m);
        for(int i(1);i<=n;++i)
            read(a[i]),dp[i]=1e11;dp[0]=0,L=1,R=0;
        for(int i(1),p(0);i<=n;++i){sum+=a[i];
            while(sum>m)sum-=a[p+1],++p;if(p==i)return puts("-1"),0;T[R+1]=i;
            while(L<=R&&T[L]<p)S.erase(S.find(dp[T[L]]+a[T[L+1]])),++L;
            while(L<=R&&a[T[R]]<a[i])S.erase(S.find(dp[T[R]]+a[T[R+1]])),--R;
            if(L<=R)S.erase(S.find(dp[T[R]]+a[T[R+1]])),S.insert(dp[T[R]]+a[i]),
                        dp[i]=*S.begin();T[++R]=i;
            if(dp[p]+a[T[L]]<dp[i])dp[i]=dp[p]+a[T[L]];S.insert(dp[i]+a[i+1]);
        }printf("%lld",dp[n]);
        return 0;
    }
    template<class free>
    il free Min(free a,free b){
        return a<b?a:b;
    }
    template<class free>
    il void read(free &x){
        x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    
    
  • 相关阅读:
    棋盘完美覆盖数(小规模原理实现)
    Codeforces 115A Party (并查集思维)
    datetime日期和时间
    range与enumerate的区别
    爬取爱套图网上的图片
    python爬取365好书中小说
    列表和元组的方法
    字符串中的方法
    从电源问题出发,带你揭秘新体系结构范式 COA
    KubeCon 2020 演讲集锦|《阿里巴巴云原生技术与实践 13 讲》开放下载
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10962007.html
Copyright © 2020-2023  润新知