粉刷木板
有 (N) 块木板从左到右排成一行,有 (m) 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。
第 (i) 个木匠要么不粉刷,要么粉刷包含木板 (S_i) 且长度不超过 (L_i) 的连续的一段木板,每粉刷一块可以得到 (P_i) 的报酬。不同工匠的 (S_i) 不同。 请问如何安排能使工匠们获得的总报酬最多。
(Nleq 16000 , Mleq 100 , S_ileq 10000)
题解
设 (dp(i,j)) 表示前 (i) 个人涂完 (j) 个木板的最大答案。
对于第 (i) 个人:
- 若他不涂,则 (dp(i,j) = dp(i-1,j)).
- 若他不涂当前木板,则 (dp(i,j)=dp(i,j-1)).
- 若他涂色,则 (dp(i,j)=max limits_{kin(0,L_i] , jin[S_i,m]}dp(i-1,j-k)+k imes P_i).
将3.
修改,为:(dp(i,j)=max limits_{kin[j-L_i.S_i)}dp(i-1,k)+(j-k) imes P_i).
则:
[dp(i,j) =maxleft{egin{matrix}
dp(i-1,j) \
dp(i,j-1)\
max limits_{kin[j-L_i.S_i)}{dp(i-1,k)+(j-k) imes P_i}
end{matrix}
ight.
]
对于以上的第三个式子,我们可以把它移项,变成
[dp(i,j)= max{j imes P_i+dp(i-1,k)-k imes P_i} , kin[j-L_i,S_i),jge S_i
]
发现式子后两项与 (j) 无关,可以用单调队列来优化此段最大值。
复杂度 (O(nm)) .
代码
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 1.6e4+5,M = 105;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret=0;char ch=' ',c=getchar();
while(!(c>='0'&&c<='9'))ch=c,c=getchar();
while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
return ch=='-'?-ret:ret;
}
int n,m;
struct node{int l,p,s;}a[M];
inline bool operator < (const node a,const node b){return a.s < b.s;}
template <typename T> struct dque{
T a[N]; int st=1,ed=0;
dque(){st=1,ed=0;}
inline void clear(){st=1,ed=0;}
inline bool empty(){return ed-st+1==0;}
inline int size(){return ed-st+1;}
inline void push_back(int x){a[++ed] = x;}
inline T front(){return a[st];}
inline T back(){return a[ed];}
inline void pop_back(){ed--;}
inline void pop_front(){st++;}
T operator [] (int x){return a[st+x-1];}
};
dque<int>q;
int dp[M][N];
signed main(){
n = read() , m = read();
for(int i = 1 ; i <= m ; i ++){
int l = read() , p = read() , s = read();
a[i] = (node){l,p,s};
}
sort(a+1,a+m+1);
for(int i = 1 ; i <= m ; i ++){
q.clear();
for(int j = max(a[i].s-a[i].l,0) ; j < a[i].s ; j ++){
while(!q.empty() && dp[i-1][q.back()] - q.back()*a[i].p < dp[i-1][j] - j*a[i].p)
q.pop_back();
q.push_back(j);
}
for(int j = 1 ; j <= n ; j ++){
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
if(j >= a[i].s){
while(!q.empty() && q.front() + a[i].l < j) q.pop_front();
if(!q.empty())dp[i][j] = max(dp[i][j],dp[i-1][q.front()] + (j-q.front())*a[i].p);
}
}
}
printf("%d",dp[m][n]);
}