[AcWing.849. Dijkstra求最短路 I]

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 -1

输入格式

第一行包含整数 nm

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 -1

数据范围

1 < n < 500,
1 < m < 10^5,
图中涉及边长均不超过10000。

输入样例:

1
2
3
4
3 3
1 2 2
2 3 1
1 3 4

输出样例:

1
3

算法思想

两个难点:

  1. 邻接表建图
  2. dijkstra算法 (参考邻接矩阵版本的)

给出变量名粗略解释:

1
2
3
4
5
int h[N];   // 顶点 i 的邻接表的首边索引
int e[M]; // 第 i 条边的终点
int ne[M]; // 第 i 条边的下一条边索引
int w[M]; // 第 i 条边的权重
int idx; // 当前边的索引

给出变量名详细解释:

1
2
3
4
5
int h[N];   // h 数组,存储每个顶点的第一条边的索引。h[i] 表示顶点 i 的第一条边在 e 数组中的索引。
int e[M]; // e 数组,存储每条边的终点。e[i] 表示第 i 条边的终点。
int ne[M]; // ne 数组,存储每条边的下一条边的索引。ne[i] 表示第 i 条边的下一条边在 e 数组中的索引。
int w[M]; // w 数组,存储每条边的权重。w[i] 表示第 i 条边的权重。
int idx; // idx 变量,记录当前边的数量,同时作为边的索引。

容易混淆的点:

  1. e[M]:如果你有一条边从顶点 u 到顶点 v,那么 e 数组的对应位置(例如 e[idx])就是顶点 v
  2. ne[M]:ne[i] 表示第 i 条边在邻接表中指向的下一条边的索引。这使得我们可以在遍历某个顶点的邻接边时,依次访问所有与该顶点相邻的边。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510; // 顶点数量上限
const int M = 100010; // 边数量上限
const int INF = 0x3F3F3F3F; // 无穷大

int h[N]; // 每个顶点的邻接表首条边的索引
int ne[M]; // 每条边的下一条边的索引
int e[M]; // 每条边的终点
int w[M]; // 每条边的权重
int idx; // 当前边的索引

// 添加一条边 a -> b,边权为 c
void add(int a, int b, int c) {
e[idx] = b; // 设置边的终点为 b
w[idx] = c; // 设置边的权重为 c
ne[idx] = h[a]; // 当前边的下一条边索引
h[a] = idx++; // 更新顶点 a 的邻接表首条边为当前边
}

int n, m, dist[N];
bool st[N];

// 使用 Dijkstra 算法计算最短路径
int dijkstra() {
memset(dist, 0x3f, sizeof dist); // 初始化距离为无穷大
dist[1] = 0; // 起点到自身的距离为 0

for (int i = 0; i < n; i++) {
int t = -1;
// 找到未标记顶点中距离起点最小的顶点
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dist[j] < dist[t])) {
t = j;
}
}

st[t] = true; // 标记顶点 t 为已访问

// 更新顶点 t 的所有邻接边的最短路径
for (int j = h[t]; ~j; j = ne[j]) {
int k = e[j]; // 邻接点
dist[k] = min(dist[k], dist[t] + w[j]); // 更新最短距离
}
}

return dist[n]; // 返回到终点 n 的最短距离
}

int main() {
memset(h, -1, sizeof h); // 初始化邻接表,表示每个顶点没有边
cin >> n >> m;

// 读入边的信息并添加到邻接表
while (m --) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c); // 添加边 a -> b,权重为 c
}

int res = dijkstra(); // 执行 Dijkstra 算法
if (res == INF) cout << "-1" << endl; // 如果无法到达终点,输出 -1
else cout << res << endl; // 输出最短距离

return 0;
}