849. Dijkstra求最短路 I
步骤:
- 单源最短路径,维护一个dist数组,记录起始点到各点的最短路径。
- 从dist中找最短的,放入S集合(flag数组记录)。这里是贪心,因为后续就算更新dist数组,也是在看从其他点绕路过去会不会距离短,但当前dist已经是数组里最短的话,也就是说所有的绕路的第一段都比当前长,故当前一定是最短,找到了,就放进S。
- 然后当前的最短的,看V-S中的点,通过最短dist绕路会不会更短。重复此步骤,直到V-S为空 or V-S中的点完全到不了S(源点V0)。
注意:
memset
只能设置为0
或者-1
重边自环:a[x][y] = min(z, a[x][y]);
- 非连通图会找不到:if (min_index == -1) break;
- dist[min_index] + a[min_index][i] < dist[i] 不是a[1][min_index] + a[min_index][i] !
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int n, m;
int a[505][505];
int dist[505];
int flag[505]; // 是否在S集合
int main() {
cin >> n >> m;
// 初始化邻接矩阵
// memset(a, INF, sizeof(a)); memset只能设置为0或者-1,所以不对
for (int i = 0; i < 505; i++)
for (int j = 0; j < 505; j++) a[i][j] = (i == j) ? 0 : INF; // 妙!
// 输入边的信息
for (int i = 0; i < m; i++) {
int x, y, z;
cin >> x >> y >> z;
a[x][y] = min(z, a[x][y]); // 可能有重边or自环
}
// 初始化距离数组和标记数组
for (int i = 1; i <= n; i++) {
dist[i] = a[1][i];
flag[i] = 0;
}
// dist[1] = 0; // 起点的距离为0,不加也行a[1][1]=0
flag[1] = 1; // 起点标记为已访问
for (int tt = 2; tt <= n; tt++) {
int min_dist = INF, min_index = -1;
for (int i = 2; i <= n; i++) {
if (flag[i] == 0 && dist[i] < min_dist) {
min_dist = dist[i];
min_index = i;
}
}
if (min_index == -1) break; // 一定要有,找不到的情况是不连通,就是在一堆INF中找最小,直接跳出就好,说明已经没有可以放进S集合的了
flag[min_index] = 1;
for (int i = 1; i <= n; i++) {
if (flag[i] == 0 && dist[min_index] + a[min_index][i] < dist[i]) {
dist[i] = dist[min_index] + a[min_index][i];
// 更新path
}
}
}
int ans = (dist[n] == INF) ? -1 : dist[n];
cout << ans;
}