POJ 3017 Cut the Sequence (单调队列优化dp)

首先我们可以推出朴素的dp转移方程 dp[i]=min(dp[j])+max(a[j+1]...a[i])

但是这个转移方程显然是超时的,所以我们需要对转移方程进行优化

做这种题目的时候,我们需要对方程进行观察,我们发现,我们需要保存两种值,一个是前面已经算过的dp方程,这个很简单

另一个是一段满足题目条件的a[i],就是最大值的关系,继而我们可以发现,如果我们对方程进行优化,我们删除的内容是要不可能取到的

换句话来说,也就是我们需要删除 s[i]-s[头部]>m的所有值,这是从头部删除数

还有队列中比a[i]小的数,因为在这个数之前的所有dp状态都取不到,我们取最大值是最优的

之后我们还需枚举从这个最大值到队末的状态,因为dp值虽然变大,但是a[i]是减小的

代码中有注释。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int q[N]; 
ll a[N];
ll s[N];
ll f[N];
int main(){
    int i;
    int n;
    ll m;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];
    }
    int tt=-1;
    int hh=0;
    int sign=0;
    int flag=0;
    for(i=1;i<=n;i++){
        while(i>sign&&s[i]-s[sign]>m)//将不满足条件的值都删掉,找到一个满足条件的位置 
        sign++;
        if(sign==i){
            flag=1;
            break;
        }
        while(hh<=tt&&a[i]>=a[q[tt]])//删除队列末小于等于他的值 
        tt--;
        q[++tt]=i;
        while(hh<=tt&&s[i]-s[q[hh]-1]>m) //删除队头不满足条件的值 
        hh++;
        f[i]=f[sign]+a[q[hh]];
        for(int j=hh;j<tt;j++){ //枚举最大值之后的所有情况 
            f[i]=min(f[i],f[q[j]]+a[q[j+1]]);
        }        
    }
    if(flag==1)
    cout<<-1<<endl;
    else
    cout<<f[n]<<endl;    
    
}
View Code

 

posted @ 2020-02-05 10:20  朝暮不思  阅读(162)  评论(0编辑  收藏  举报