Loading

:D 获取中...

Solution - HDU 6157 The Karting

帅哥 Vjudge:Link

一开始真的被抽象到了。首先因为权值可能是负的,肯定不能贪心;也没有其它好的想法,考虑 dp。

但是怎么设计状态呢?如果单纯记录路径(比如记录终点啥的),那么判不了重。我们考虑,路径怎么走的并不重要,我们只关心在哪些点处改变了方向(拐点)。分析样例,走的 \(1 \to 2 \to 3 \to 2 \to 3 \to 4 \to 3 \to 2 \to 1\)

1 2 3 4
->->x
  x<-
  ->->x
x<-<-<-

只关心拐点 \(1, 3, 2, 4\),可以发现,对于往左再往右中间的点(如 \(1, 2\)),贡献是的两端前缀路径长度加上改变方向所用费用;对于往右再往左中间的点(如 \(3, 4\)),贡献是的两端前缀路径长度加上改变方向所用费用。通过这样对拐点费用的加减,就可以正确地维护每条路径的贡献。所以只需要依次计算关键点,加上贡献,就可以了,自然不会重复。这也就是费用提前计算

\(f_{i, j, k}\) 表示前 \(i\) 个点,选出 \(j\) 个关键点,往左再往右中间的拐点数量减去往右再往左中间拐点的数量为 \(k\) 时,最大的贡献。因为我们一定先走的是“往左再往右”,一直有 \(k \geq 0\)。注意关键点并非一定是拐点,可以在中间!!!

转移分为不选第 \(i\) 个点为关键点;选第 \(i\) 个点为关键点,不是拐点(上面强调的);选第 \(i\) 个点为关键点,是往左再往右 / 往右再往左拐点的情况。方程懒得写,比较 naive。

namespace liuzimingc {
#define endl '\n'
const int N = 105;

int n, m, d[N], D, f[N][N][N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	while (cin >> n >> m) {
		cin >> D;
		for (int i = 2; i <= n; i++) cin >> d[i], d[i] += d[i - 1];
		memset(f, -0x3f, sizeof(f));
		f[0][0][0] = 0;
		for (int i = 1; i <= n; i++) {
			f[i][0][0] = 0;
			for (int j = 1; j <= i; j++)
				for (int k = 0; k <= j; k++) {
					f[i][j][k] = max(f[i][j][k], f[i - 1][j][k]); // 不选
					f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k]); // 选,不是拐点
					f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k + 1] + D + 2 * d[i]); // 往右再往左
					if (k)
						f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k - 1] + D - 2 * d[i]); // 往左再往右
				}
		}
		cout << f[n][m][0] << endl;
	}
	return 0;
}
} // namespace liuzimingc

upd:更抽象的一点。

posted @ 2024-07-11 22:09  liuzimingc  阅读(8)  评论(0编辑  收藏  举报