原题链接:Acwing 849. Dijkstra求最短路 I
给定一个 n 个点 m 条边的 有向图 ,图中可能存在 重边 和 自环 ,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n≤500,
1≤m≤105,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
方法一:Dijkstra算法(朴素版)
思路:
首先,判断为稠密图,适合邻接矩阵来存储图
Dijkstra算法的思路:
- dist[]初始化为无穷 dist[1] = 0
- for(循环n次):
- 找到不在集合st[]中的距离最近的点t
- 将t加入集合
- 用t去更新其他点到起点的最短距离
关于有向图:
- 无向图可以看做特殊的有向图,无向边可以看做是两条两个方向的有向边。因此无向图和有向图的算法没有区别
关于自环:
- 只要没有负权值,自环显然不会算入最短路,因此不需要特殊处理
关于重边:
- 对于两个顶点之前的边,只要邻接矩阵中存的是最短的那一条即可
c++ 代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, m; //n顶点数 m边数
int g[N][N]; // 稠密图 用邻接矩阵
int dist[N]; // 到源点的最短距离
bool st[N]; // 已经确定最短距离的点的集合
// 求1号点到n号点的最短路径长度
int dijkstra(){
// 第一步:初始化距离dist[], dist[0]为0, 其余都为正无穷
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
// 循环n次
for(int i = 0; i < n; i++ ){
// 第二步: 从当前未确定最短路的点中找到距离最近的点t
int t = -1;
for(int j = 1; j <= n; j++ ){
if(!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
}
// 第三步:将t加入到集合s中
st[t] = true;
// 第四步: 用当前结点更新其余顶点到源点的最短距离
for(int j = 1; j <= n; j++ )
// 这里就是和prim的区别
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
if(dist[n] == INF) return -1;
return dist[n];
}
int main(){
scanf("%d%d", &n, &m);
// 邻接矩阵初始化都为无穷
memset(g, 0x3f, sizeof g);
// 读入m条边
while(m--){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
// 因为有重边, 因此只要保留最短的那一条边即可
g[a][b] = min(g[a][b], c);
}
int t = dijkstra();
printf("%d\n", t);
return 0;
}
复杂度分析:
- 空间复杂度:
O(n^2)
,花在邻接矩阵上 - 空间复杂度:
O(n^2)
,两重循环