poj 1821(单调队列优化DP)

poj 1821(单调队列优化DP)

题意:K个人对N块木板涂色,每个人初始站在一块木板前(不重复),每人最多只能涂包含所站木板的连续l个木板或一个木板也不涂。给出每人最多涂的木块数l,涂一快木板的工钱p,站的木板s。求这群人最多共获得多少工钱

思路:状态转移方程只差一点点就对了…我是想 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i个人涂完,最后涂的是j,状态方程的前两个我因此没能想到…我还是陷入了思维定势,事实上不用这么麻烦,我们在多阶段决策的时候,中间有些是不涂的,所有直接 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前i个人涂前j个的最大值

d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j1] 第j个不涂

d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j] 第i个人直接不涂

d p [ i ] [ j ] = j ∗ p i + m a x ( d p [ i − 1 ] [ k ] − k ∗ p i ) ( j − l i ≤ k ≤ s i − 1 ) dp[i][j]=j*p_i+max(dp[i-1][k]-k*p_i)(j-l_i\leq k\leq s_i-1) dp[i][j]=jpi+max(dp[i1][k]kpi)(jliksi1) 除了这两种情况后,第j个必定涂,涂 [ k − 1 , j ] [k-1,j] [k1,j]

显然固定 i i i,第三个方程的下标是递增的,符合单调队列的使用条件,然后就秒了,得注意下第三个转移的条件,

j < = s i j<=s_i j<=si才能转移,时间复杂度 O ( N M ) O(NM) O(NM)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=16005;
int n,k,dp[105][maxn],h,t;
struct Node{ 
    int l,p,s;
    bool operator<(const Node&a)const{ 
        return s<a.s;
    }
}node[105];
struct Q{ 
    int val,id;
    Q(){ }
    Q(int x,int y){ 
        val=x;id=y;
    }
}q[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>k;
    for(int i=1;i<=k;++i){ 
        cin>>node[i].l>>node[i].p>>node[i].s;
    }
    sort(node+1,node+1+k);
    for(int i=1;i<=k;++i){ 
        h=1;t=0;
        for(int j=max(0,node[i].s-node[i].l);j<=node[i].s-1;++j){ //入队
                Q tmp(dp[i-1][j]-j*node[i].p,j);
                while(h<=t&&q[t].val<=tmp.val)t--;
                q[++t]=tmp;
        }
        for(int j=1;j<=n;++j){
            dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
            if(j>=node[i].s){ 
                while(h<=t&&q[h].id<j-node[i].l)h++;//排除队头不合法决策
                if(h<=t)
                    dp[i][j]=max(dp[i][j],j*node[i].p+q[h].val);
            }
        }
    }
    cout<<dp[k][n];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值