单源最短路径,Dijkstra | 洛谷 P4779 【模板】单源最短路径(标准版)
题意
给定一个 \(n\) 个点,\(m\) 条有向边的带非负权图,请你计算从 \(s\) 出发,到每个点的距离。
数据保证你能从 \(s\) 出发到任意点。
分析
关于 SPFA,它死了。
Dijkstra 算法由荷兰计算机科学家 E. W. Dijkstra 于 1956 年发现,1959 年公开发表。是一种求解 非负权图 上单源最短路径的算法。优先队列优化的 Dijkstra 算法的时间复杂度为 \(O(m \log m)\)。
然后重复以下操作:
- 每次寻找一个 \(dis\) 最小的点,然后对所有出边进行 松弛(relax) 操作。即对于每个点 \(u\),都用它将相邻的所有点 \(v\) 的 \(dis\) 值更新的过程。具体来说,更新过程为 \(\min(dis_v,\space dis_u + w_{u, v})\)。
直到松弛操作扩散到所有的点,算法结束。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
struct edge{
int to, w, nxt;
}e[N];
int n, m, u, v, w, s, head[N], dis[N];
bool vis[N];
inline void add_edge(int u, int v, int w, int id){
e[id] = {v, w, head[u]};
head[u] = id;
}
struct node{
int id, dis; // 结构体记录每个点的编号和最短路长度
bool operator < (const node &x) const{ // 重载运算符
return dis > x.dis;
}
};
inline void Dijkstra(int s){
priority_queue<node> q;
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
dis[s] = 0, q.push({s, 0}); // 将起点放入队列
while(!q.empty()){ // 当队列非空(即没有遍历完所有的店)
u = q.top().id; q.pop(); // 取出队首(由于使用了优先队列,队首即为 dis 最小的点)
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt){ // 遍历所有出边
v = e[i].to;
if(dis[v] > dis[u] + e[i].w){ // 松弛操作
dis[v] = dis[u] + e[i].w;
if(!vis[v]) q.push({v, dis[v]}); // 遍历到 v,则将 v 也加入队列
}
}
}
return;
}
int main(){
ios::sync_with_stdio(false);
memset(head, -1, sizeof head);
cin >> n >> m >> s;
for(int i = 1; i <= m; i++){
cin >> u >> v >> w;
add_edge(u, v, w, i);
}
Dijkstra(s);
for(int i = 1; i <= n; i++)
cout << dis[i] << ' '; // 依次输出每个点的 dis 值
return 0;
}
望穿寂夜晨曦至,雄鹰展翅图九天。

浙公网安备 33010602011771号