あいさか たいがblogAisaka_Taiga的博客
//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

Johnson 全源最短路

Toretto·2023-10-06 19:14·330 次阅读

Johnson 全源最短路

Johnson 全源最短路

Johnson 和 Floyd 一样是能求出无负环图上任意两点间最短路径的算法。

引入#

求任意两点间的最短路可以通过枚举起点,跑 n 次 SPFA 来解决,时间复杂度是 O(n2m) 的,也可以用 Floyd 解决,复杂度为 O(n3)

或者我们可以跑 n 次堆优化的 Dijkstra,复杂度为 O(nmlogm)

但是 Dijkstra 有一个致命的缺陷就是他不能处理负边权。

我们不难想到来修改边权使其为正数。

核心思想#

我们新建一个虚拟的节点,假设他的编号为 0,从这个点向其他所有点连一条边权为 0 的边。

接下来我们跑一遍 SPFA,求出零号点到所有点的最短路记为 hi,顺便判断一下有没有负环。

如果存在一条边 (u,v,w),我们将其修改为 (u,v,w+hihv)

接下来以每一个点为起点跑 n 边 Dijkstra 就好了。

复杂度为 O(nmlogm)

正确性#

我们考虑找到从 st 的一条路径为:

sp1p2pkt

那么这条路径的长度就是:

(w(s,p1)+hshp1)+(w(p1,p2)+hp1hp2)++(w(pk,t)+hpkht)

展开就是:

w(s,p1)+w(p1,p2)++w(pk,t)+hsht

所以无论怎么走,只要是 st 的一条最短路径,那么最后就是比原答案多了 hsht

Q:你说的对,但是为什么能保证修改后的边权都是非负数?

根据 hvhu+w(u,v),稍微变化一下就是 hu+w(u,v)hv0,所以图中的边权均为非负。

code#

Copy
#include <bits/stdc++.h> #define pii pair<int, int> #define INF 1000000000 #define int long long #define N 10010 #define endl '\n' using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();} while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return x * f; } int n, m, t, head[N], vis[N], cnt[N], h[N], d[N], tot; struct node{int v, next, w;}e[N << 4]; inline void add(int u, int v, int w){e[++ tot] = (node){v, head[u], w}; head[u] = tot;} inline int spfa(int s) { queue<int> q; memset(vis, 0, sizeof vis); for(int i = 1; i <= n; i ++) h[i] = INF; h[s] = 0; vis[s] = 1; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i; i = e[i].next) { int v = e[i].v, w = e[i].w; if(h[v] > h[u] + w) { h[v] = h[u] + w; if(!vis[v]) { vis[v] = 1; cnt[v] ++; q.push(v); if(cnt[v] == n + 1) return 0; } } } } return 1; } inline void dijkstra(int s) { priority_queue<pii, vector<pii>, greater<pii> > q; memset(vis, 0, sizeof vis); for(int i = 1; i <= n; i ++)d[i] = INF; d[s] = 0; q.push({0, s}); while(!q.empty()) { int u = q.top().second; q.pop(); if(vis[u]) continue ; vis[u] = 1; for(int i = head[u]; i; i = e[i].next) { int v = e[i].v, w = e[i].w; if(d[v] <= d[u] + w) continue; d[v] = d[u] + w; q.push({d[v], v}); } } return ; } signed main() { n = read(), m = read(); for(int i = 1; i <= n; i ++) add(0, i, 0); for(int i = 1; i <= m; i ++) { int u = read(), v = read(), w = read(); add(u, v, w); } if(!spfa(0)) return cout << "-1" << endl, 0;//负环输出0 for(int u = 1; u <= n; u ++) for(int i = head[u]; i; i = e[i].next) e[i].w += h[u] - h[e[i].v];//修改边权 for(int i = 1; i <= n; i ++) { dijkstra(i); int ans = 0; for(int j = 1; j <= n; j ++) { if(d[j] == INF) ans += j * INF; else ans += j * (d[j] + h[j] - h[i]); } cout << ans << endl; } return 0; }

参考文章:https://zhuanlan.zhihu.com/p/99802850

posted @   北烛青澜  阅读(330)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-10-06 搜索学习笔记
点击右上角即可分享
微信分享提示
目录