POJ 1821

题目链接

这道题是很经典的一道维护单调队列的题目,最近在做POJ的题目。记录一下这道题。

题意是$K$个工人,$N$个木板。每个工人可以最多涂$L_i$个木板,涂木板的区间必须是连续的,且必须包含$S_i$位置的木板,涂每块木板的收入是$P_i$,求这些工人最多的收入是多少。

DP。DP[i][j]表示i个工人涂j个木板的最多收入。则有

$DP[i][j] = max(dp[i-1][j],dp[i][j-1], dp[i-1][k] + peo[i].pay*(j-k))$

这里 $dp[i-1][k] + peo[i].pay*(j-k))$表示前面$i-1$个人涂了$k$个木板,第$i$个工人涂接下来的$j-k$个。

会发现此时$dp[i-1][k] + peo[i].pay*(j-k)) = dp[i-1][k] - peo[i].pay*k+ peo[i].pay*j$ 。前面的$ dp[i-1][k] - peo[i].pay*k$可以采用单调队列解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
int n,k;
struct po {
int len;
int st;
int pay;
}peo[200];

bool cmp(po p1,po p2) {
return p1.st < p2.st;
}

int dp[105][17000];
deque<int>q;

void solve() {
sa(n),sa(k);
repp(i,1,k) {
sa(peo[i].len);
sa(peo[i].pay);
sa(peo[i].st);
}
sort(peo+1,peo+k+1,cmp);

repp(i,1,k) {
q.push_front(max(0,peo[i].st-peo[i].len));
repp(j,1,n) {
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
if(j >= peo[i].st + peo[i].len)continue;
while(!q.empty() && q.front() + peo[i].len < j)
q.pop_front();

if(j < peo[i].st) {
int t = dp[i-1][j] - peo[i].pay*j;
while(!q.empty() && dp[i-1][q.back()] - peo[i].pay*q.back()< t)
q.pop_back();

q.push_back(j);
continue;
}
int ans = 0;
if(!q.empty())
ans = dp[i-1][q.front()] + peo[i].pay*(j-q.front());

dp[i][j] = max(dp[i][j],ans);
}
}
printf("%d\n",dp[k][n]);
}