ABC219H Candles
[ABC219H] Candles
很像 关路灯 这玩意儿啊,容易想到区间 \(DP\)
仿照那道题,可以考虑设出状态,即 当前已经熄掉的区间 \([L, R]\),与 当前所在端点 \(0/1\)
我们有如下转移
但是这题的区别就在于 蜡烛是会熄灭的
也就是如果按 上式转移,当发生蜡烛已经熄灭时人才到位的情况时,贡献会错
于是我们可以考虑 多设一种状态,用 \(k\) 表示 之后还有多少蜡烛要被熄灭
每次的转移我们考虑 这个蜡烛是否已经熄灭,换言之就是 是否要被熄灭
-
如果其已经熄灭,即 它不用被熄灭(之后还需熄灭的蜡烛不变,故从 \(k\) 转移过来)
则 \(f_{l, r, k, 0}\) 将从 \(f_{l + 1, r, k, 0}, f_{l + 1, r, k ,1}\) 中 转移,且 不会加上这个蜡烛的贡献 \(A_l\)
而 \(f_{l, r, k, 1}\) 将从 \(f_{l, r - 1, k, 0}, f_{l, r - 1, k, 1}\) 中 转移,且 不会加上这个蜡烛的贡献 \(A_r\)
-
如果其还没熄灭,即 现在把它熄灭(之后还需熄灭的蜡烛 \(-1\),故从 \(k + 1\) 转移过来)
则 \(f_{l, r, k, 0}\) 将从 \(f_{l + 1, r, k + 1, 0}, f_{l + 1, r, k + 1, 1}\) 中 转移,且 应当加上这个蜡烛的贡献 \(A_l\)
而 \(f_{l, r, k, 1}\) 将从 \(f_{l, r - 1, k + 1, 0}, f_{l, r - 1, k + 1, 1}\) 中 转移,且 应当加上这个蜡烛的贡献 \(A_r\)
而由于我们当前有 \(k\) 个蜡烛 还需熄灭,也就是 只考虑这 \(k\) 个蜡烛 还在燃烧
故我们的 花费(\(Dis\))应当乘上 \(k\) 这个系数,而非之前的 \(N - r + l\)(所有没到达的)
于是最终转移式如下
然后我们可以写出这样的代码
注意到 剩余需要熄灭的蜡烛 不可能比 剩余蜡烛 多,所以枚举 \(k\) 的时候可以 适当剪枝,见下
#include <bits/stdc++.h>
const int MAXN = 305;
using namespace std;
long long F[MAXN][MAXN][MAXN][2];
struct Candle {
int x, l;
inline bool operator < (const Candle &a) const {
return x < a.x;
}
} C[MAXN];
inline long long Dis (const int L, const int R) {
return 0ll + C[R].x - C[L].x;
}
int N, T, r;
signed main () {
cin >> N;
for (int i = 1; i <= N; ++ i) cin >> C[i].x >> C[i].l;
C[++ N] = {0, 0};
sort (C + 1, C + N + 1);
for (int i = 1; i <= N; ++ i) if (!C[i].l) {T = i; break ;}
memset (F, -127, sizeof F);
for (int i = 0; i <= N; ++ i) F[T][T][i][0] = F[T][T][i][1] = 0;
for (int len = 2; len <= N; ++ len) {
for (int l = 1; l + len - 1 <= N; ++ l) {
r = l + len - 1;
for (int k = 0; k < min (N, l + N - r); ++ k) {
F[l][r][k][0] = max ({F[l + 1][r][k][0] - 1ll * k * Dis (l, l + 1), F[l + 1][r][k][1] - 1ll * k * Dis (l, r), F[l + 1][r][k + 1][0] - 1ll * (k + 1) * Dis (l, l + 1) + C[l].l, F[l + 1][r][k + 1][1] - 1ll * (k + 1) * Dis (l, r) + C[l].l});
F[l][r][k][1] = max ({F[l][r - 1][k][0] - 1ll * k * Dis (l, r), F[l][r - 1][k][1] - 1ll * k * Dis (r - 1, r), F[l][r - 1][k + 1][0] - 1ll * (k + 1) * Dis (l, r) + C[r].l, F[l][r - 1][k + 1][1] - 1ll * (k + 1) * Dis (r - 1, r) + C[r].l});
}
}
}
cout << max (F[1][N][0][0], F[1][N][0][1]) << endl;
return 0;
}
而由于我们这里数组有 \(300 \times 300 \times 300 \times 2 = 5.4 \times 10 ^ 7\) 的大小,还是 long long
所以其 访问速度 差到了极致,慢的飞起
但由于这是 区间 \(DP\),我们注意到 len 单调变化,并且右端点 \(r\) 可以通过 \(l + len - 1\) 唯一表示
所以我们可以 省掉一个维度,每次 ++ len 的时候 滚动更新
#include <bits/stdc++.h>
const int MAXN = 305;
using namespace std;
long long G[MAXN][MAXN][2], Z[MAXN][MAXN][2];
struct Candle {
int x, l;
inline bool operator < (const Candle &a) const {
return x < a.x;
}
} C[MAXN];
inline long long Dis (const int L, const int R) {
return 0ll + C[R].x - C[L].x;
}
int N, T, r, t;
signed main () {
cin >> N;
for (int i = 1; i <= N; ++ i) cin >> C[i].x >> C[i].l;
C[++ N] = {0, 0};
sort (C + 1, C + N + 1);
for (int i = 1; i <= N; ++ i) if (!C[i].l) {T = i; break ;}
memset (Z, -127, sizeof Z);
memset (G, -127, sizeof G);
for (int i = 0; i <= N; ++ i) Z[T][i][0] = Z[T][i][1] = G[T][i][0] = G[T][i][1] = 0;
for (int len = 2; len <= N; ++ len) {
for (int l = 1; l + len - 1 <= N; ++ l) {
r = l + len - 1, t = l + N - r;
for (int k = 0; k < min (N, t); ++ k) {
G[l][k][0] = max ({Z[l + 1][k][0] - 1ll * k * Dis (l, l + 1), Z[l + 1][k][1] - 1ll * k * Dis (l, r), Z[l + 1][k + 1][0] - 1ll * (k + 1) * Dis (l, l + 1) + C[l].l, Z[l + 1][k + 1][1] - 1ll * (k + 1) * Dis (l, r) + C[l].l});
G[l][k][1] = max ({Z[l + 0][k][0] - 1ll * k * Dis (l, r), Z[l + 0][k][1] - 1ll * k * Dis (r - 1, r), Z[l + 0][k + 1][0] - 1ll * (k + 1) * Dis (l, r) + C[r].l, Z[l + 0][k + 1][1] - 1ll * (k + 1) * Dis (r - 1, r) + C[r].l});
}
}
memcpy (Z, G, sizeof G);
}
cout << max (G[1][0][0], G[1][0][1]) << endl;
return 0;
}

浙公网安备 33010602011771号