把学校当作唯一源点,先跑一次堆优化 Dijkstra,再用 dist[h] 回答所有同学。
P11966 [GESP202503 八级] 上学
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
每来一个同学就从家重新跑最短路,会把 q 次查询放大成 q 次 Dijkstra。
二、核心观察
点是路口,边是道路长度;学校 s 是源点,dist[u] 表示 u 到学校的最短时间。
为什么不丢答案:无向图中从 s 到 h 的距离等于从 h 到 s 的距离,所以一次单源最短路覆盖所有查询。
源点 s
小根堆
dist[h]
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么从学校出发也能回答同学到学校?
先判断,再点按钮。
为什么不能用 Floyd?
先判断,再点按钮。
五、手推结果
| 步骤 | 保留的信息 | 压缩掉的信息 |
|---|---|---|
| 建图 | 邻接表 | 完整矩阵 |
| Dijkstra | 每点最短 dist | 每条候选路径 |
| 回答 | dist[h] | 重新搜索过程 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
struct Edge {
int to;
int weight;
};
struct Node {
int id;
ll dist;
bool operator>(const Node& other) const {
return dist > other.dist;
}
};
int main() {
// 加速输入输出
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, s, q;
cin >> n >> m >> s >> q;
vector<vector<Edge>> adj(n + 1);
for (int i = 0; i < m; i++) {
int u, v, l;
cin >> u >> v >> l;
adj[u].push_back({v, l});
adj[v].push_back({u, l});
}
// Dijkstra 算法
vector<ll> dist(n + 1, INF);
priority_queue<Node, vector<Node>, greater<Node>> pq;
dist[s] = 0;
pq.push({s, 0});
while (!pq.empty()) {
Node current = pq.top();
pq.pop();
int u = current.id;
ll d = current.dist;
if (d > dist[u]) continue;
for (auto& edge : adj[u]) {
int v = edge.to;
int weight = edge.weight;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.push({v, dist[v]});
}
}
}
// 处理查询
for (int i = 0; i < q; i++) {
int h;
cin >> h;
cout << dist[h] << "\n";
}
return 0;
}