cf 1077 区间dp 双向队列保存区间最大值

http://codeforces.com/contest/1077/problem/F2

题意:给定n,k,x; 表示长为n的序列中,每长为k的一段必须有一个数被选择

  在选择x次之后,得到的最大值

思路:简单的n,k,x<=200的时候 可以nkx的dp

  但是对于nkx<=4000的  这样就超市

但是我们可以上面的思路修改一下

d[i][j]表示 第i次选择,选择最后面的位置为j 这时候的最大值

  d[0][0]=0;

    for(int i=1;i<=x;++i){
        for(int j=1;j<=n;++j)
            for(int q=1;q<=k;++q)
                if(j-q<0)continue;
                else if(d[i-1][j-q]!=-1)
                d[i][j] = max(d[i-1][j-q]+a[j],d[i][j]);
    }

上面是nkx的复杂度

 

针对于内层循环,我们可以改进一下:

内层选择的 :    在符合<=k的条件下,取出这个区间的最大值

那么如果用双向队列(i前面的数已经入队)

1.那么把  j-Q.front()>k  的进行pop_back(),这样剩下的都是符合条件的了

2.那如何保证一个最大值呢?

假设我们这次走到了 d[i][j] 那么应该讲 d[i-1][j-1]push进去

如果在此之前  将尾部所有比d[i-1[j-1]的 元素pop_back()  

在最初 deque是个递减的 那么这样叠加下去,仍然是递减的

所以deque里的头部永远是这一段的最大值

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define time __time

const int N  =2e5+4;
ll a[N],b[N];

ll d[5222][5222];

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,x,k;
    cin>>n>>k>>x;

    for(int i=1;i<=n;++i)cin>>a[i];
    if( n/k>x  ){
        printf("-1\n");
        return 0;
    }
    memset(d,-1,sizeof(d));

    d[0][0]=0;

    deque<int>Q;
    for(int i=1;i<=x;++i){
        for(int j=1;j<=n;++j){
            while(!Q.empty() &&j-Q.front()>k )
                Q.pop_front();
            while(!Q.empty() &&d[i-1][Q.back()] <= d[i-1][j-1] )
                Q.pop_back();
            Q.push_back(j-1);
            if(d[i-1][Q.front()]!=-1)
                d[i][j] = d[i-1][Q.front()]+a[j];
        }
        while(!Q.empty())Q.pop_back();
    }
    ll ans = -1;
    for(int q=n;q>n-k;--q){
        ans = max(ans,d[x][q]);
    }
    cout<<ans<<endl;

    return 0;
}

 

posted on 2018-11-19 21:45  Helpp  阅读(255)  评论(0编辑  收藏  举报

导航