[poj3017]Cut the Sequence——单调队列优化DP

题目大意:

给定一个长度为n的序列和整数m,求一个划分使得每一段的和不大于m且每一段的最大值的和最小。

思路:

设dp[i]为前i个的最小和,方程显而易见:

d p [ i ] = m i n ( d p [ j ] + max ( a [ j + 1 ] . . . a [ i ] ) )

朴素的时间复杂度 O ( n 2 )
考虑怎么求一段连续区间的最大值,发现可以用一个单调递减的队列维护,但是这样还是优化不了时间。发现dp数组同样满足单调递增,于是对于单调递减的队列中的每一个元素所对应一段区间,它的dp的最小值一定在区间的最前端取到,这样一来单调队列中的每一个元素都对应了唯一的一个dp值。由于这是单调队列,所以每一个元素只会进队一次出队一次,所以只要维护这个单调队列以及这个单调队列中的最大值就好了,用一个set来维护最大值。
然而实际测得直接扫一遍比用set要快不知道多少。

/*=================================
 * Author : ylsoi
 * Problem : Cutting The Sequence
 * Algorithm : DP
 * Time : 2018.5.6
 * ===============================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<set>
#include<deque>
#include<ctime>
using namespace std;
void File(){
    freopen("[poj3017]cutting_the_sequence.in","r",stdin);
    freopen("[poj3017]cutting_the_sequence.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define fuck(...) printf(__VA_ARGS__)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf LLONG_MAX
void read(ll &x){
    ll sum=0ll;
    char c=getchar();
    while(c<'0' || c>'9')c=getchar();
    while(c>='0' && c<='9'){
        sum=(sum<<1)+(sum<<3)+(c^'0');
        c=getchar();
    }
    x=sum;
}
const int maxn=1e5+10;
int n;
ll m,a[maxn],sum[maxn],dp[maxn];
void init(){
    scanf("%d%lld",&n,&m);
    REP(i,1,n)read(a[i]);
    REP(i,1,n)sum[i]=sum[i-1]+a[i];
}
deque<int>qu;
multiset<ll>s;
multiset<ll>::iterator it;
void del(ll x){it=s.lower_bound(x);s.erase(it);}
void work(){
    REP(i,1,n)if(a[i]>m){puts("-1");return;}
    int p=0;
    REP(i,1,n){
        int tmp=p;
        while(sum[i]-sum[tmp]>m)++tmp;
        while(qu.size() && qu.front()<=tmp){
            del(a[qu.front()]+dp[p]);
            p=qu.front();
            qu.pop_front();
        }
        if(qu.size()){
            del(a[qu.front()]+dp[p]);
            s.insert(a[qu.front()]+dp[tmp]);
        }
        p=tmp;
        while(qu.size() && a[qu.back()]<=a[i]){
            int bb=qu.back();
            qu.pop_back();
            del(a[bb]+dp[qu.empty() ? p : qu.back()]);
        }
        s.insert(a[i]+dp[qu.empty() ? p : qu.back()]);
        qu.push_back((int)i);
        dp[i]=*s.begin();
        /*dp[i]=inf;
        REP(j,0,(int)qu.size()-1){
            if(j==0)dp[i]=min(dp[i],dp[p]+a[qu[j]]);
            else dp[i]=min(dp[i],dp[qu[j-1]]+a[qu[j]]);
        }*/
    }
    printf("%lld\n",dp[n]);
}
int main(){
    File();
    init();
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80215738