Johnson全源最短路
板子题:https://www.luogu.com.cn/problem/P5905
个人认为是最难的一个最短路算法了,因为实现中用到了很多其他的最短路
数据:
负权边:支持
判断负环:不支持
复杂度:
(实际上是
前置知识
没错要学这个最短路其他的都得会。
基本思路
通过复杂度可以看出,这个算法与
那为什么不用
那怎么解决呢?重新标权值,且保证正确性。
加一个新点0,将0与每个点之间连一条长度为0的边。
然后求0到每个点的最短路,记0到 i 的最短路为
更新每个边
三角形两边之和大于第三边,所以
(
所以这个图上每个边就都是非负的了,就可以跑
然后把每个源点的最短路
正确性证明
设起始点为
最短路:
原图中的最短路长度:
新图中的最短路长度:
你会发现,带
则新图中最短路长度为:
又因为两点间势能的差为定值,即
所以新图中的最短路与原图中的最短路对应。
实现思路
求0到每个点的最短路,因为图中有负权,可以用
但这样连出来的图一定是个菊花图,选择这两种的效率没什么区别。
而且这不影响最后的时间复杂度,时间复杂度由
然后枚举点u,再枚举所有能到达的点v,更新u到v的权值w。
最后跑
另外,对于这道题,要判断负环。
可以在求0到每个点的最短路时顺便用
代码
#include <iostream>
#include <queue>
#include <utility>
#include <functional>
#include <cstring>
using namespace std;
struct Edge
{
int v, w, nxt;
}edge[9001];
int n, m, u, v, w, head[3001], cnt;long long dis[3001], h[3001], ans;
void add(int u, int v, int w) //加边
{
++cnt;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
void dijkstra(int s) //正常的dijkstra
{
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;bool vis[3001];memset(vis, 0, sizeof vis);
for(int i = 1;i <= n;++i) dis[i] = 1e9;
dis[s] = 0;q.push(make_pair(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 = edge[i].nxt)
if(dis[edge[i].v] > dis[u] + edge[i].w)
{
dis[edge[i].v] = dis[u] + edge[i].w;
q.push(make_pair(dis[edge[i].v], edge[i].v));
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0;i < m;++i)
cin >> u >> v >> w, add(u, v, w);
for(int i = 1;i <= n;++i)
add(0, i, 0), h[i] = 1e9; //加从0到每个点的边,权值为0
h[0] = 0;
for(int i = 1;i < n;++i) //开始bellman-ford
for(int u = 0;u <= n;++u)
for(int j = head[u];j;j = edge[j].nxt)
h[edge[j].v] = min(h[edge[j].v], h[u] + edge[j].w);
for(int u = 1;u <= n;++u) //判负环
for(int i = head[u];i;i = edge[i].nxt)
if(h[edge[i].v] > h[u] + edge[i].w)
{
cout << -1;
return 0;
}
for(int u = 1;u <= n;++u)
for(int i = head[u];i;i = edge[i].nxt)
edge[i].w = edge[i].w + h[u] - h[edge[i].v]; //更新权值
for(int u = 1;u <= n;++u)
{
dijkstra(u);ans = 0;
for(int v = 1;v <= n;++v)
if(dis[v] == 1e9) ans += v * 1e9;
else ans += v * (dis[v] - h[u] + h[v]); //还原权值
cout << ans << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具