- hzoi044 的博客
最短路算法
- @ 2024-7-27 11:19:53
最短路算法
多源最短路
Floyd
- 适用范围:无负权回路即可,边权可正可负,运行一次算法即可求得任意两点间最短路。
- 用f(i,j,k)表示只考虑前k个点,i到j的最短路
枚举k作为中转点
从i点到j点,可以直接到达,也可以选择通过k点中转后到达
如果通过k点中转后,从i到j的距离缩短,那么就将f(i,j,k)更新为f(i,k,k−1)+f(k,j,k−1)
所以状态转移方程为,f(i,j,k)=min(f(i,j,k−1),f(i,k,k−1)+f(k,j,k−1))
又由于k仅与k−1有关,所以可以用滚动数组优化掉这一维
进行滚动数组优化后,可得代码
for(int k = 1;k<=n;k++){ for(int i = 1;i<=n;i++){ for(int j = i;j<=n;j++){ f[i][j] = min(f[i][j],f[i][k]+f[k][j]); } }}
单源最短路
Dijkstra
- 大概是这么个流程

-
//结构体Node用来记录点的位置与当前最短路struct Node{ int dis,pos; friend bool operator < (Node A,Node B){ return A.dis > B.dis; }};//优先队列,每次取当前路径最短的点priority_queue<Node>q;void Dijkstra(){ //每个点到自己的路径长度为0 //s为源点 dis[s] = 0; //源点到源点的最短路为0 q.push((Node){0,s}); while(!q.empty()){ //弹出元素为到源点路径最短的点 Node tmp = q.top(); q.pop(); int x = tmp.pos; int d = tmp.dis; //如果这个点曾经访问过则跳过 if(vis[x]) continue; //标记这个点访问过 vis[x] = 1; //链式向前星存图 for(int i = head[x];i;i = edge[i].nxt){ //枚举当前点可以到达的点y_ int y_ = edge[i].to; //如果从源点到y_的距离长于从源点先到x再从x到y_的距离 //即从源点到y_的距离可以通过x点缩短时,更新最短路 if(dis[y_] > dis[x] + edge[i].dis){ dis[y_] = dis[x] + edge[i].dis; //如果y_这个点没有到达过,将其压入队列 //现在可以通过y_中转到目标点了 if(!vis[y_]) q.push((Node){dis[y_],y_}); } } }}
SPFA

经典咏流传
- SPFA一般用于Dijkstra不适用的地方,比如负权图、判断负环等
-
queue<int> q;int dis[N];int vis[N];int ans = 0;void SPFA(int s){ vis[s] = 1; q.push(s); while(!q.empty()){ int t = q.front(); q.pop(); vis[t] = 0; for(int i = head[t];i;i=edge[i].nxt){ int to_ = edge[i].to; int dis_ = edge[i].w; if(dis[to_]>dis[t]+dis_){ dis[to_] = dis[t]+dis_; if(!vis[to_]){ q.push(to_); vis[to_] = 1; } } } } return;}