ABC219H Candles

[ABC219H] Candles

很像 关路灯 这玩意儿啊,容易想到区间 \(DP\)

仿照那道题,可以考虑设出状态,即 当前已经熄掉的区间 \([L, R]\),与 当前所在端点 \(0/1\)

我们有如下转移

\[f_{l, r, 0} = \max (f_{l + 1, r, 0} - (N - r + l) * Dis (l, l + 1), f_{l + 1, r, 1} - (N - r + l) * Dis (l, r)) \\ f_{l, r, 1} = \max (f_{l, r - 1, 0} - (N - r + l) * Dis (l, r), f_{l, r - 1, 1} - (N - r + l) * Dis (r - 1, r)) \]

但是这题的区别就在于 蜡烛是会熄灭的

也就是如果按 上式转移当发生蜡烛已经熄灭时人才到位的情况时,贡献会错

于是我们可以考虑 多设一种状态,用 \(k\) 表示 之后还有多少蜡烛要被熄灭

每次的转移我们考虑 这个蜡烛是否已经熄灭,换言之就是 是否要被熄灭

  1. 如果其已经熄灭,即 它不用被熄灭(之后还需熄灭的蜡烛不变,故从 \(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\)

  2. 如果其还没熄灭,即 现在把它熄灭(之后还需熄灭的蜡烛 \(-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\)所有没到达的

于是最终转移式如下

\[f_{l, r, k, 0} = \max (f_{l + 1, r, k, 0} - k * Dis (l, l + 1), f_{l + 1, r, k, 1} - k * Dis (l, r), f_{l + 1, r, k + 1, 0} - (k + 1) * Dis (l ,l + 1) + A_l, f_{l + 1, r, k + 1, 1} - (k + 1) * Dis (l, r) + A_l) \\ f_{l, r, k, 1} = \max (f_{l, r - 1, k, 0} - k * Dis (l, r), f_{l, r - 1, k, 1} - k * Dis (r - 1, r), f_{l, r - 1, k + 1, 0} - (k + 1) * Dis (l, r) + A_r, f_{l, r - 1, k + 1, 1} - (k + 1) * Dis (r - 1, r) + A_r) \]

然后我们可以写出这样的代码

注意到 剩余需要熄灭的蜡烛 不可能比 剩余蜡烛 多,所以枚举 \(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;
}
posted @ 2024-03-06 16:12  FAKUMARER  阅读(34)  评论(0)    收藏  举报