单调队列优化dp+决策单调性——poj3017好题

无人久伴 提交于 2020-03-17 17:49:32
/*
dp[i]表示前i个数的最优解
dp[i]=min{ dp[j]+max[j+1,i] },sum[i]-sum[j]<=M 
使用决策单调性:显然dp[i]是单调递增的
    那么维护一个区间[j+1,i]关于a的递减单调队列q
    对于a[q[1]]可以控制的决策范围是[j+1,q[1]],又因为决策单调性,所以这一段区间的最优解就是dp[j+1]+a[q[1]]
    同理,遍历整个队列,可得后面每一段的最佳决策
*/ 
#include<iostream>
#include<queue>
#include<cstdio> 
using namespace std;
#define ll long long 
#define N 200005

ll n,M,a[N],sum[N],dp[N];

int main(){
    cin>>n>>M;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=n;i++)if(a[i]>M){
        puts("-1");return 0;
    }
    
    memset(dp,0x3f,sizeof dp);dp[0]=0;
    deque<int>q;
    deque<int>::iterator it,itt;
    int pos=1;
    for(int i=1;i<=n;i++){
        while(sum[i]-sum[pos-1]>M)pos++;
        while(q.size()){//删前面 
            int p=q.front();
            if(sum[i]-sum[p-1]>M)q.pop_front();
            else break;
        }
        while(q.size()){//删后面 
             int p=q.back();
             if(a[p]<=a[i])q.pop_back();
             else break;
        }
        q.push_back(i);
        for(it=q.begin();it!=q.end();it++){
            if(it==q.begin()){
                dp[i]=min(dp[i],a[*it]+dp[pos-1]);
            }else {
                itt=it;itt--;
                dp[i]=min(dp[i],a[*it]+dp[*itt]);
            }
        }
    }
    cout<<dp[n]<<'\n';
} 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!