>Link
ybtoj粉刷木板
>Description
有 N 块木板从左到右排成一行,有 M 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。
第 i 个木匠要么不粉刷,要么粉刷包含木板 s i s_i si 且长度不超过 l i l_i li 的连续的一段木板,每粉刷一块可以得到 p i p_i pi 的报酬。不同工匠的 p i p_i pi 不同。 请问如何安排能使工匠们获得的总报酬最多。
N ≤ 16000 , M ≤ 100 N\le 16000,M\le 100 N≤16000,M≤100
>解题思路
我们可以进行DP,设 f i , j f_{i,j} fi,j为前 i i i个木板,让前 j j j个木匠来刷的最大总报酬
f i , j = m a x ( f i − 1 , j , f i , j − 1 , ( i − l i ≤ k < s j ) f k , j − 1 + p j ∗ ( i − k ) ) f_{i,j}=max(f_{i-1,j},f_{i,j-1},(i-l_i \le k<s_j)f_{k,j-1}+p_j*(i-k)) fi,j=max(fi−1,j,fi,j−1,(i−li≤k<sj)fk,j−1+pj∗(i−k))
情况一:第 i i i块木板不刷;情况二:第 j j j个木匠不刷;情况三:第 j j j个木匠刷区间 [ k + 1 , i ] [k+1,i] [k+1,i]的木板
情况一二很好处理,我们现在优化情况三
把式子拆开 f i , j = p j ∗ i + m a x ( f k , j − 1 − p j ∗ k ) f_{i,j}=p_j*i+max(f_{k,j-1}-p_j*k) fi,j=pj∗i+max(fk,j−1−pj∗k)
对于后面这个包含 k k k的部分,我们发现 k k k的范围是不断后移的,所以我们可以搞一个单调队列来维护它
不知道为什么打的时候输出那里出锅了,要拿个ans记录 f i , j f_{i,j} fi,j的最大值输出才能A QwQ(然后我改了好久
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 16010
#define LL long long
using namespace std;
struct node
{
int l, s; LL p;
} a[N];
int n, m, head, tail, pos[N], l, r;
LL f[N][110], q[N], ans;
bool cmp (node aa, node bb) {
return aa.s < bb.s;}
int main()
{
scanf ("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf ("%d%lld%d", &a[i].l, &a[i].p, &a[i].s);
sort (a + 1, a + 1 + m, cmp);
for (int j = 1; j <= m; j++)
{
head = 1, tail = 0;
memset (q, 0, sizeof (q));
memset (pos, 0, sizeof (pos));
for (int i = max (0, a[j].s - a[j].l); i < a[j].s; i++) //可以选的k的范围,先把最前面的那段范围放进来(后面慢慢把左端点后移)
//因为我们发现k的范围的右端点是不会变化的,所以我们后续就不用扩大单调队列了
{
LL sum = f[i][j - 1] - a[j].p * (LL)i;
while (head <= tail && q[tail] < sum) tail--;
tail++;
q[tail] = sum, pos[tail] = i;
}
for (int i = 1; i <= n; i++)
{
f[i][j] = max (f[i - 1][j], f[i][j - 1]);
ans = max (ans, f[i][j]);
}
for (int i = a[j].s; i <= min (n, a[j].s + a[j].l - 1); i++) //i的范围
{
int k = max (0, i - a[j].l);
while (head <= tail && pos[head] < k) head++;
if (head <= tail)
f[i][j] = max (f[i][j], q[head] + a[j].p * (LL)i);
ans = max (ans, f[i][j]);
//for (int k = max (0, i - a[j].l); k < a[j].s; k++)
// f[i][j] = max (f[i][j], f[k][j - 1] + a[j].p * (i - k));
}
}
printf ("%lld", ans);
return 0;
}