1 条题解
-
0
摧毁时间线 题解
题目分析
我们有一个长度为 的时间线,每个时刻 有一个值 。还有三组控制器的位置 。
移除时刻 的能量消耗为:
$$(a_{i-1}' - x_i)^2 + (a_i - y_{i+1})^2 + (a_{i+1}' - z_{i+2})^2 $$其中 表示:如果 这个时刻还没有被移除,则为 ,否则为 。 同理。
目标是找到一个移除顺序,使得总能量消耗最大。
关键洞察:如果我们考虑最后被移除的时刻,它会将时间线分成左右两个独立的子区间。这是典型的区间 DP 结构。
算法设计
状态定义
设 表示将原始区间 内的所有时刻移除所能获得的最大总能量,其中:
- 表示 可用(还没被移除), 表示不可用(已被移除或不存在)
- 表示 可用, 表示不可用
状态转移
枚举区间 中最后一个被移除的时刻 :
- 移除 时, 可用的条件是: 且
- 可用的条件是: 且
移除 的代价为:
$$cost = (A - x_k)^2 + (a_k - y_{k+1})^2 + (B - z_{k+2})^2 $$其中:
- (如果 可用),否则
- (如果 可用),否则
总能量为:
$$dp[l][r][left][right] = \max_{k=l}^{r} \left[ dp[l][k-1][left][0] + dp[k+1][r][0][right] + cost \right] $$边界条件
- 当 时,返回
- 初始调用:(整个区间的左右邻居都存在且值为 )
- 需要处理好边界下标,设置 ,
复杂度分析
- 状态数:
- 每个状态转移:
- 总时间复杂度:,在 时完全可行
- 空间复杂度:
C++ 实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = 1e18; int n; ll a[65], x[65], y[65], z[65]; ll dp[65][65][2][2]; bool vis[65][65][2][2]; ll dfs(int l, int r, int la, int ra) { if (l > r) return 0; if (vis[l][r][la][ra]) return dp[l][r][la][ra]; ll &res = dp[l][r][la][ra]; res = -INF; for (int k = l; k <= r; ++k) { // 计算移除 k 的代价 ll A = 0, B = 0; if (k == l && la) A = a[l-1]; // a_{k-1} 可用 if (k == r && ra) B = a[r+1]; // a_{k+1} 可用 ll cost = (A - x[k]) * (A - x[k]) + (a[k] - y[k+1]) * (a[k] - y[k+1]) + (B - z[k+2]) * (B - z[k+2]); ll left_val = dfs(l, k-1, la, 0); // 左边区间,右邻居不可用 ll right_val = dfs(k+1, r, 0, ra); // 右边区间,左邻居不可用 res = max(res, left_val + right_val + cost); } vis[l][r][la][ra] = true; return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; for (int i = 1; i <= n; ++i) cin >> x[i]; for (int i = 1; i <= n; ++i) cin >> y[i]; for (int i = 1; i <= n; ++i) cin >> z[i]; // 边界处理 a[0] = a[n+1] = 0; y[n+1] = 0; z[n+1] = z[n+2] = 0; memset(vis, 0, sizeof(vis)); // 初始状态:整个区间 [1, n],左右邻居都存在(值为0) ll ans = dfs(1, n, 1, 1); cout << ans << endl; return 0; }算法标签
- 区间动态规划
- 记忆化搜索
- 最优化顺序问题
- 状态压缩
- 1
信息
- ID
- 5633
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 6
- 已通过
- 1
- 上传者