Solution - HDU 6157 The Karting
帅哥 Vjudge:Link。
一开始真的被抽象到了。首先因为权值可能是负的,肯定不能贪心;也没有其它好的想法,考虑 dp。
但是怎么设计状态呢?如果单纯记录路径(比如记录终点啥的),那么判不了重。我们考虑,路径怎么走的并不重要,我们只关心在哪些点处改变了方向(拐点)。分析样例,走的 :
1 2 3 4
->->x
x<-
->->x
x<-<-<-
只关心拐点 ,可以发现,对于往左再往右中间的点(如 ),贡献是负的两端前缀路径长度加上改变方向所用费用;对于往右再往左中间的点(如 ),贡献是正的两端前缀路径长度加上改变方向所用费用。通过这样对拐点费用的加减,就可以正确地维护每条路径的贡献。所以只需要依次计算关键点,加上贡献,就可以了,自然不会重复。这也就是费用提前计算。
设 表示前 个点,选出 个关键点,往左再往右中间的拐点数量减去往右再往左中间拐点的数量为 时,最大的贡献。因为我们一定先走的是“往左再往右”,一直有 。注意关键点并非一定是拐点,可以在中间!!!
转移分为不选第 个点为关键点;选第 个点为关键点,不是拐点(上面强调的);选第 个点为关键点,是往左再往右 / 往右再往左拐点的情况。方程懒得写,比较 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 by liuzimingc
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY