无垢清净光,慧日破诸暗,能伏灾风火,普明照世间。——法华经

Solution - HDU 6157 The Karting

帅哥 Vjudge:Link

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

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

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

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

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

转移分为不选第 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 @   liuzimingc  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示