AcWing 298 围栏
暴力
思路
首先将每个木匠按 \(s_i\) 升序排序。
设 \(dp_{i, j}\) 为前 \(i\) 个工匠涂前 \(j\) 个木板所获得的最大收益(注意: 有些木板可以不涂,或者说最大收益的涂法 涂不到 某些木板)。
所以我们可以得到如下转移方程:
若第 \(i\) 个木工选择不涂或 \(j < s_i\),\(dp_{i, j} = dp_{i - 1, j}\);
若第 \(j\) 块木板不涂(或者说涂不到),那么答案就是涂前 \(j - 1\) 块木板的最大收益,即:\(dp_{i, j} = dp_{i, j - 1}\);
不然,设 \(k\) 为【木工 \(i\)】【开始涂木板的位置】的上一位】,那么:
\[dp_{i, j} = max_{k = s_i - l_i}^{s_i - 1} \begin{Bmatrix} dp_{i - 1, k} + (j - k) * p_i \end{Bmatrix}
\]
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 7;
const int maxm = 1.6e4 + 7;
int n, m;
struct node {
int l, s, p;
} a[maxn];
bool cmp(node x, node y) {return x.s < y.s;}
int dp[maxn][maxm];
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &a[i].l, &a[i].p, &a[i].s);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[i][j] = dp[i - 1][j];
if (a[i].s > j) continue;
// 涂不到
if (j - a[i].l + 1 > a[i].s) dp[i][j] = max(dp[i][j], dp[i][j - 1]);
else { // 能涂到
for (int k = a[i].s; k >= max(1, j - a[i].l + 1); --k)
dp[i][j] = max(dp[i][j], dp[i - 1][k - 1] + (j - k + 1) * a[i].p);
}
}
}
printf("%d\n", dp[n][m]);
return 0;
}
时间复杂度 \(O(nm^2)\)。
正解
思路
把转移方程变一下:
\[dp_{i, j} = max_{k = s_i - l_i}^{s_i - 1} \begin{Bmatrix} dp_{i - 1, k} - k * p_i \end{Bmatrix} + j * p_i
\]
发现转移就是求 \(i - 1\) 行的合法范围内的最大值,用单调队列解决。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 7;
const int maxm = 1.6e4 + 7;
int n, m;
struct node {
int l, s, p;
} a[maxn];
bool cmp(node x, node y) {return x.s < y.s;}
int dp[maxn][maxm];
int q[maxm], h, t;
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &a[i].l, &a[i].p, &a[i].s);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
// 在 s[i] 之前的位置只能由前 i - 1 个工匠涂
for (int j = 1; j < a[i].s; ++j) dp[i][j] = dp[i - 1][j];
// 先处理好等会要用到的前驱状态
h = 1, t = 0;
for (int j = max(0, a[i].s - a[i].l); j < a[i].s; ++j) {
while (h <= t && dp[i - 1][j] - j * a[i].p > dp[i - 1][q[t]] - q[t] * a[i].p) --t;
q[++t] = j;
}
for (int j = a[i].s; j <= min(m, a[i].s + a[i].l - 1); ++j) {
while (h <= t && q[h] < j - a[i].l) ++h;
dp[i][j] = max(dp[i - 1][j], dp[i - 1][q[h]] + (j - q[h]) * a[i].p);
}
for (int j = a[i].s + a[i].l; j <= m; ++j)
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
printf("%d\n", dp[n][m]);
return 0;
}

浙公网安备 33010602011771号