把每块挡板的左右端点看成图上的状态点,在状态图上求从起点到目标挡板的最短时间。
P10726 [GESP202406 八级] 空间跳跃
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
只按高度贪心往下掉,可能错过需要先在挡板上横向移动的更短路线。
二、核心观察
状态是“在哪块挡板的哪一端”;边分为板内横走和从端点竖直掉落。
为什么不丢答案:每一次真实移动都能对应状态图中的一条边,反过来状态图路径也都能实际执行。
端点状态
掉落目标
最短时间
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么要把左右端点分开?
先判断,再点按钮。
到达 t 挡板时还要走到端点吗?
先判断,再点按钮。
五、手推结果
| 边类型 | 代价 | 作用 |
|---|---|---|
| 板内横走 | r-l | 左右端点互达 |
| 竖直掉落 | 高度差 | 落到下方挡板 |
| 目标判定 | 到 t | 更新答案 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e18;
struct Board {
int id;
int l, r, h;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, s, t;
cin >> n >> s >> t;
vector<Board> b(n + 1);
for (int i = 1; i <= n; i++) {
b[i].id = i;
cin >> b[i].l >> b[i].r >> b[i].h;
}
if (s == t) {
cout << 0 << endl;
return 0;
}
// dist[i][0] 表示到达第 i 个挡板左端点的最短时间
// dist[i][1] 表示到达第 i 个挡板右端点的最短时间
vector<vector<long long>> dist(n + 1, vector<long long>(2, INF));
priority_queue<pair<long long, pair<int, int>>, vector<pair<long long, pair<int, int>>>, greater<pair<long long, pair<int, int>>>> pq;
dist[s][0] = 0;
pq.push({0, {s, 0}});
long long min_to_t = INF;
while (!pq.empty()) {
auto curr = pq.top();
pq.pop();
long long d = curr.first;
int u = curr.second.first;
int side = curr.second.second; // 0: left, 1: right
if (d > dist[u][side]) continue;
// 在挡板内移动到另一端
int other_side = 1 - side;
long long move_cost = b[u].r - b[u].l;
if (dist[u][other_side] > d + move_cost) {
dist[u][other_side] = d + move_cost;
pq.push({dist[u][other_side], {u, other_side}});
}
// 掉落
int x = (side == 0 ? b[u].l : b[u].r);
int target_board = -1;
int max_h = -1;
for (int i = 1; i <= n; i++) {
if (b[i].h < b[u].h && x >= b[i].l && x <= b[i].r) {
if (b[i].h > max_h) {
max_h = b[i].h;
target_board = i;
}
}
}
if (target_board != -1) {
long long fall_dist = b[u].h - b[target_board].h;
if (target_board == t) {
min_to_t = min(min_to_t, d + fall_dist);
} else {
// 掉到 target_board,可以选择去它的左端点或右端点
long long to_l = d + fall_dist + (x - b[target_board].l);
if (dist[target_board][0] > to_l) {
dist[target_board][0] = to_l;
pq.push({dist[target_board][0], {target_board, 0}});
}
long long to_r = d + fall_dist + (b[target_board].r - x);
if (dist[target_board][1] > to_r) {
dist[target_board][1] = to_r;
pq.push({dist[target_board][1], {target_board, 1}});
}
}
}
}
if (min_to_t == INF) cout << -1 << endl;
else cout << min_to_t << endl;
return 0;
}