1 条题解
-
0
「POI 2020/2021 R1」Gra platformowa 题解
算法思路分析
关键观察
1. 洞的处理顺序
vector<pair<int, int>> holes; for (int i = 1; i <= n; i++) { int k; cin >> k; while (k--) { int x; cin >> x; holes.push_back({-x, i}); // 存储负值便于从右向左排序 } } sort(holes.begin(), holes.end());
- 将洞按位置从右向左排序(通过存储负值实现)
- 这样处理时能保证先处理靠近终点的洞
2. 状态定义与转移
vector<int> f(n + 1); for (auto [x, i] : holes) { f[i]++; // 遇到洞,该层跳跃次数+1 if (i + 1 <= n) { int z = min(f[i], f[i + 1]); f[i] = f[i + 1] = z; // 合并相邻层的答案 } }
状态含义:
- 表示从第 层平台当前处理位置到达终点所需的最小跳跃次数
转移逻辑:
- 遇到洞时:
- 因为必须用一次跳跃来通过这个洞
- 合并相邻层:
- 利用 操作可以在相邻层间转移
- 取最小值保证最优性
算法正确性证明
为什么从右向左处理?
- 终点在位置 ,从右向左处理确保每个洞的处理都基于其后继状态的最优解
- 符合动态规划的"后效性"消除原则
为什么合并相邻层?
考虑相邻的两层平台 和 :
- 通过 操作,可以从第 层跳到第 层
- 反之,从第 层也可以通过某种方式影响第 层
- 取最小值确保了两层之间的最优连通性
复杂度分析
- 时间复杂度:
- 排序洞:
- 处理洞:
- 回答查询:
- 空间复杂度:
完整代码解释
#include <bits/stdc++.h> using namespace std; signed main() { ios::sync_with_stdio(false); cin.tie(0); int n, X, z; cin >> n >> X >> z; vector<pair<int, int>> holes; // 读取所有洞,存储为(负位置, 层号) for (int i = 1; i <= n; i++) { int k; cin >> k; while (k--) { int x; cin >> x; holes.push_back({-x, i}); // 负号实现从右向左排序 } } // 按位置从右向左排序 sort(holes.begin(), holes.end()); // f[i]: 从第i层当前处理位置到终点的最小跳跃次数 vector<int> f(n + 1); // 从右向左处理每个洞 for (auto [x, i] : holes) { f[i]++; // 遇到洞,需要一次跳跃 // 合并相邻层的答案 if (i + 1 <= n) { int z = min(f[i], f[i + 1]); f[i] = f[i + 1] = z; } } // 回答查询 while (z--) { int p; cin >> p; cout << f[p] << "\n"; // 输出从第p层起点到终点的答案 } }
算法优势
- 简洁高效:代码非常简短,但正确解决了问题
- 利用题目性质:充分利用了"洞不相邻"的条件
- 线性复杂度:相对于平台长度 是 的
- 支持多查询:预处理后可以 回答每个查询
适用场景
这种方法特别适用于:
- 很大(达到 )的情况
- 洞的总数 在可接受范围内
- 需要支持大量查询
- 1
信息
- ID
- 3310
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者