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][j−1] 第j个不涂
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i−1][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]=j∗pi+max(dp[i−1][k]−k∗pi)(j−li≤k≤si−1) 除了这两种情况后,第j个必定涂,涂 [ k − 1 , j ] [k-1,j] [k−1,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;
}