引言
近期 南昌市中小学生信息学奥林匹克竞赛 报名正在进行(比赛时间 2026 年 10 月 25 日,报名窗口 9 月 29 日---10 月 16 日),其提高组考察范围明确把「图论算法」列入重点。而在图论里,单源最短路是最基础、也最常考的模型之一:给定一张带权图和一个起点,求起点到所有点的最短距离。
今天我们用一道原创题「校园科技节赛场路线规划」带你吃透 迪杰斯特拉(Dijkstra)算法,并给出 C++ 与 Python 双语言实现、考点拆解、易错点与进阶方向。

一、题目 / 项目目标
【原创题】校园科技节赛场路线规划
校园科技节在建,共有 n 个场地(编号 1~n),校门在 1 号场地。任意两个场地之间可能有一条双向通道,走完需要 t 分钟(t>0)。作为志愿者调度员,你需要算出从校门(1 号)到每个场地的最短通行时间;特别地,到 k 号比赛主场馆的最短时间是多少?
- 输入:
- 第一行 n, m, k(场地数、通道数、目标场馆编号,2 ≤ n ≤ 10⁵,1 ≤ m ≤ 2×10⁵)
- 接下来 m 行,每行 u, v, t(u、v 之间有一条耗时 t 分钟的双向通道,1 ≤ t ≤ 10⁴)
- 输出:
- 第一行输出 n 个整数,第 i 个为校门到 i 号场地的最短时间;若不可达输出 -1。
- 第二行输出校门到 k 号场馆的最短时间(不可达输出 -1)。
样例输入
5 6 5
1 2 2
1 3 4
2 3 1
2 4 7
3 4 3
4 5 1
样例输出
0 2 3 6 7
7
样例解释: 1→2 最短 2;1→2→3 最短 2+1=3(比直连 1→3 的 4 更优);到 4 走 1→2→3→4 = 2+1+3 = 6;到 5 再经 4 加 1 = 7。所以第一行 0 2 3 6 7,第二行 7。
二、核心考点
- 单源最短路模型与松弛操作(relaxation) :用
dis[v] = min(dis[v], dis[u] + w)不断缩短距离。 - Dijkstra 的贪心正确性 :每次从「未确定」集合中取出当前距离最小的点,在边权非负的前提下,它的距离就已经是最短,可以永久确定。
- 堆(优先队列)优化:把朴素算法 O(V²) 的「找最小点」降为 O((V+E) log V)。
- 邻接表存图:稀疏图必须用邻接表,否则空间与时间都会炸。
- 已确定集合(vis 标记):避免同一个点被重复扩展。
- 不可达判定 :最终
dis仍为 INF 的点即不可达,输出 -1。 - 距离可能很大 :累加后超过 int 范围,应使用
long long或大整数。
三、解法 / 拆解
思路
- 用邻接表
g[u]存(v, w)这种「邻居 + 边权」对。 dis[i]初始全为 INF,dis[1] = 0。- 小根堆存放元组
(距离, 节点),每次取出距离最小的节点 u: - 若 u 已被确定(visu 为真)则跳过------堆里可能残留它更早、更大的旧距离副本;
- 否则标记 u 已确定,遍历它的所有邻居 v,若
dis[u] + w < dis[v]就松弛并把新距离压入堆。 - 最后按格式输出
dis数组,仍为 INF 的输出 -1 即可。
C++ 实现(堆优化)
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 1e18;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
if (!(cin >> n >> m >> k)) return 0;
vector<vector<pair<int, int>>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v, t;
cin >> u >> v >> t;
g[u].emplace_back(v, t);
g[v].emplace_back(u, t); // 双向通道,存两次
}
vector<ll> dis(n + 1, INF);
vector<bool> vis(n + 1, false);
dis[1] = 0;
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<>> pq;
pq.emplace(0, 1);
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (vis[u]) continue; // 旧副本,跳过
vis[u] = true;
for (auto [v, w] : g[u]) {
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
pq.emplace(dis[v], v);
}
}
}
for (int i = 1; i <= n; i++) {
cout << (dis[i] == INF ? -1 : dis[i]) << (i == n ? "" : " ");
}
cout << "\n" << (dis[k] == INF ? -1 : dis[k]) << "\n";
return 0;
}
Python 实现(heapq)
python
import sys
import heapq
def main():
data = sys.stdin.read().split()
if not data:
return
it = iter(data)
n = int(next(it)); m = int(next(it)); k = int(next(it))
g = [[] for _ in range(n + 1)]
for _ in range(m):
u = int(next(it)); v = int(next(it)); t = int(next(it))
g[u].append((v, t))
g[v].append((u, t)) # 双向通道,存两次
INF = float('inf')
dis = [INF] * (n + 1)
vis = [False] * (n + 1)
dis[1] = 0
pq = [(0, 1)]
while pq:
d, u = heapq.heappop(pq)
if vis[u]:
continue
vis[u] = True
for v, w in g[u]:
if dis[u] + w < dis[v]:
dis[v] = dis[u] + w
heapq.heappush(pq, (dis[v], v))
out = []
for i in range(1, n + 1):
out.append("-1" if dis[i] == INF else str(dis[i]))
print(" ".join(out))
print(-1 if dis[k] == INF else dis[k])
if __name__ == "__main__":
main()
四、时间与空间复杂度
- 时间复杂度 :每个节点入堆一次、每条边松弛一次,每次堆操作 O(log V),总计 O((V + E) log V)。
- 空间复杂度 :邻接表 O(V + E),距离数组 O(V),堆最坏 O(E),合计 O(V + E)。
- 选型提示:朴素 Dijkstra(未优化)为 O(V²),只适合稠密图(E ≈ V²);本题 E 可达 2×10⁵、V 达 1e5,必须用堆优化版本。
五、易错点
- 负权边:Dijkstra 要求边权非负。一旦出现负权,贪心「取最小即确定」不再成立,应改用 Bellman-Ford / SPFA(注意 SPFA 在稠密图或特殊构造下可能退化为 O(VE))。
- 堆里的旧副本 :同一个节点可能被多次压入(距离不断变小),弹出时必须用
vis[u]判断并跳过,否则会重复扩展、甚至死循环。 - INF 取值与溢出 :
dis累加可能超过 int,请用long long或 Python 大整数;INF取1e18量级,避免INF + w上溢变成负数。 - 重边与自环:邻接表直接存即可,算法天然处理重边(取更优者);自环不影响结果。
- 起点初始化 :
dis[起点] = 0千万别漏,否则全部仍是 INF。 - 双向通道:无向图要把边加两次,漏掉一次会直接 WA。
六、进阶
- 最短路计数 :松弛时若
dis[u] + w == dis[v]则把方案数累加(取模),注意要在「确定」顺序下合并,防止重复计数。 - 严格次短路 :维护
dis1[v](最短路)与dis2[v](严格次短),松弛时同时更新两者。 - 路径还原 :用
pre[v]记录最短路上 v 的前驱,从终点沿pre回溯即得具体路线。 - 多源最短路(多个校门):建一个超级源点 S,向每个起点连一条权 0 的边,跑一次 Dijkstra 即可统算。
- 与其他算法对比:无权图用 BFS(O(V+E) 更快);全源最短路用 Floyd(O(V³),适合 V 很小);带负权用 Bellman-Ford / SPFA。
- 练习推荐:洛谷 P4779(标准堆优化模板)、P1339、P1462(二分答案 + 最短路)等。
七、小结与互动
迪杰斯特拉是图论最短路的基石,核心就三件事:贪心取最小 + 堆优化降复杂度 + 松弛更新距离。把它和 BFS(无权)、Floyd(全源)放到一张对照表里记忆,考试遇到「路线规划」「最少耗时」「最小代价」类题目就能快速选型。
你还有哪些图论算法想让我拆解?欢迎在评论区留言,下一篇我们可以讲最短路计数 或严格次短路的实战题。
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:
- 全国青少年信息素养大赛复赛集训题目Python&C++.docx
https://pan.quark.cn/s/93995d3cb150 - 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
https://pan.quark.cn/s/da97b5dbf75d - Python背记手册.pdf
https://pan.quark.cn/s/7568ae9ca92b - Python课程
https://pan.quark.cn/s/a94bf02d00c6 - 2024信息素养大赛图形化复赛集训题答案3-9
https://pan.quark.cn/s/6ccab7ec3cbc - 2025年03月份电子学会考级真题
https://pan.quark.cn/s/4403c4228912 - 2025全国青少年信息素养大赛赛项说明
https://pan.quark.cn/s/d9d0df4a9f29 - 青少儿信息素养大赛编程资料
https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注获取最新分享。