Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记
如果你希望得到带互动的极简文字体验,请点这里
我们来学习johnson
Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法。它允许一些边权重为负数,但可能不存在负权重循环。它的工作原理是使用Bellman-Ford 算法来计算输入图的转换,该转换去除了所有负权重,从而允许在转换后的图上使用Dijkstra 算法。Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法。它允许一些边权重为负数,但可能不存在负权重循环。它的工作原理是使用Bellman-Ford 算法来计算输入图的转换,该转换去除了所有负权重,从而允许在转换后的图上使用Dijkstra 算法。
--维基百科
- 可以发现排序算法中最能够全源的只有Johnson 和 Floyd
最优情况:
全源最短路跑\(n\) 次 Bellman-Ford 算法解决,时间复杂度是\(O(n^2m)\),原始是Floyd的\(O(n^3)\)
我们可以更优,使用迪杰斯特拉\(O(nm \space logm)\)
但是我们还关注能否处理负边
所以只有SPFA,Floyd,贝尔曼,Johnson可以解决
原理解释
所以我们解决带负边的全源最短路时同时兼顾时间应该怎么做?
- 使用Dijkstra
但是Dijkstra不能处理有负边的问题,所以我们可以使边变为正数,我们基本得到3个猜想:
- 对每条边进行相同的增量,使所有边非负。
- 改变图的结构,不改变图的性质,使Dijkstra可做。
- 对每条边单独设计增量,使图的性质保证并且全图非负。
可以看出只有第三种是正确的。
为什么第一种不正确?
如上图(箭头错,1->3应该是3->1)3->2的最短路原本是-2(走上面),但是对于边权+5的图(绿)就成了9(走下面),路径发生了改变,不可取。
Johnson算法核心
建一个超级源,到所有点连0 权边,对超级源跑一遍SPFA,求出每个点的dis 值。重构原图边权,对于边 , , ,将其边权修改为 + dis − dis 。
由三角不等式: + dis ≥ dis ,从而新的边权一定是非负数。
在新图上跑全源 Dijkstra,以 \(u_0\) 为起点,假设经过 \(u_1\), \(u_2\), … , \(u_{t-1}\) 到达 \(u_t\) 。
则经过的边权分别为 $w_{12} $ \(-dis u_2 + w_{2,3}\) $ + dis u_2 − dis u_3 + ⋯ + $ $w_{t−1},t + dis u_{t−1} -dis u_t $
\]
只需将跑出的最短路结果做 \(− (dis u_1 − dis u_t )\) 变换就可以得到真实最短路。
正确性
为什么我们使用这个方法是正确的
在重新加权的图中,节点对s和t之间的所有路径都添加了相同的数量\(h ( s ) - h ( t )\)。
正确当且仅当重新加权后的最短路径是原始的最短路径。
\(dis_v \ge dis_u+e_w\)因此,不可能有负边:如果边 \(u \to v\) 在重新加权后具有负权重,那么从 \(q \to u\) 的零长度路径与这条边将形成从 \(q \to v\) 的负长度路径,这与以下事实相矛盾所有顶点到 \(q\) 的距离为零。
负边的不存在确保了 Dijkstra 算法找到的路径的最优性。
原始图中的距离可以通过逆重加权变换从重加权图中的Dijkstra算法计算出的距离计算出来。
Johnson 算法的前三个阶段如下图所示
图中左侧的图形有两个负边,但没有负循环。
中心图显示了新的顶点 \(q\) ,一个最短路径树,由 Bellman-Ford 算法计算,\(q\) 作为起始顶点,每个其他节点计算的值 \(h ( v )\) 作为从 \(q\) 到该节点的最短路径的长度节点。
请注意,这些值都是非正数,因为q到每个顶点都有一条长度为零的边,并且最短路径不能长于该边。
右侧显示了重新加权的图,通过替换每个边的权重形成 \({\displaystyle w(u,v)}{\displaystyle w(u,v)}\) 由 \(w ( u , v ) + h ( u ) − h ( v )\)。
在这个重新加权的图中,所有边的权重都是非负的,但任意两个节点之间的最短路径使用与原始图中相同两个节点之间的最短路径相同的边序列。该算法最后将 Dijkstra 算法应用于重新加权图中的四个起始节点中的每一个。
#include<bits/stdc++.h>
#define ll long long
#define fd(i, a, b) for (ll i = a; i >= b; i--)
#define r(i, a) for (ll i = fir[a]; i; i = e[i].nex)
#define file(a) freopen(#a ".in", "r", stdin);
#define il inline
#define gc getchar()
#define f(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
const ll maxn=3e5+10,INF=1e16;
il ll read(){
ll x=0,f=1;char ch=gc;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc;}
while(ch>='0'&&ch<='9') x=(x*10)+(ch^48),ch=gc;
return x*f;
}
ll fir[maxn],n,m,cnt;
struct edge{ll to,nex,w;}e[maxn<<1];
il void add(ll a,ll b,ll c){e[++cnt].to=b,e[cnt].w=c,e[cnt].nex=fir[a];fir[a]=cnt;}
bool vis[maxn];
ll far[maxn];
ll num[maxn];
il bool spfa(ll x){
queue<ll> q;
memset(far,0x7f,sizeof(far));
far[x]=0;
q.push(x);
while(!q.empty()){
ll u=q.front();
// cout<<far[u]<<endl;
// cout<<u<<endl;
vis[u]=0;
if(++num[u]>n) return 0;
q.pop();
r(i,u){
ll v=e[i].to;
// cout<<e[i].w<<endl;
if(far[v]>far[u]+e[i].w){
far[v]=far[u]+e[i].w;
// cout<<far[v]<<endl;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return 1;
}
priority_queue<pair<ll,ll> >q;
ll dis[maxn],cop;
il void dijkstra(ll x){
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
cop=dis[0];
dis[x]=0;
q.push(make_pair(0,x));
while(!q.empty()){
ll u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
r(i,u){
ll v=e[i].to;
// cout<<e[i].w<<endl;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
q.push(make_pair(-dis[v],v));
}
}
}
}
ll Add=1e9;
int main()
{
n=read(),m=read();
f(i,1,m){
ll x=read(),y=read(),z=read();
add(x,y,z);
}
f(i,1,n) add(0,i,0);
// cout<<1;
if(spfa(0)){
f(i,1,n) r(j,i){
// cout<<far[i]-far[e[j].to]<<endl;
e[j].w+=far[i]-far[e[j].to];
// cout<<e[j].w<<endl;
}
// f(i,1,n) cout<<far[i]<<" ";
// cout<<endl;
f(i,1,n){
ll ans=0;
dijkstra(i);
f(j,1,n){
if(dis[j]==cop) ans+=j*Add;
else ans+=j*(dis[j]+far[j]-far[i]);
}
printf("%lld\n",ans);
}
}
else printf("-1");
}
参考文献
1.Johnson's algorithm--wikipedia
Johnson 全源最短路径算法学习笔记的更多相关文章
- Johnson 全源最短路径算法
解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: ...
- Floyd-Warshall 全源最短路径算法
Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E) 上每对顶点间的最短路径问题,即全源最短路径问题(All-Pairs Shortest Paths Probl ...
- 【学习笔记】 Johnson 全源最短路
前置扯淡 一年多前学的最短路,当时就会了几个名词的拼写,啥也没想过 几个月之前,听说了"全源最短路"这个东西,当时也没说学一下,现在补一下(感觉实在是没啥用) 介绍 由于\(spf ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
- Johnson全源最短路
例题:P5905 [模板]Johnson 全源最短路 首先考虑求全源最短路的几种方法: Floyd:时间复杂度\(O(n^3)\),可以处理负权边,但不能处理负环,而且速度很慢. Bellman-Fo ...
- Johnson 全源最短路
学这个是为了支持在带负权值的图上跑 Dijkstra. 为了这个我们要考虑把负的权值搞正. 那么先把我们先人已经得到的结论摆出来.我们考虑先用 SPFA 对着一个满足三角形不等式的图跑一次最短路,具体 ...
- Dijkstra 单源最短路径算法
Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...
- Bellman-Ford 单源最短路径算法
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法.该算法由 Richard Bellman 和 Leste ...
随机推荐
- redux的使用
1.redux的使用 核心概念 action 动作的对象 包含2个属性 type:标识属性, 值为字符串, 唯一, 必要属性 data:数据属性, 值类型任意, 可选属性 例子:{ type: 'AD ...
- 前端~定位属性position(relative、absolute、fixed)的分析
前端~定位属性position(relative.absolute.fixed)的分析 1,简单了解: relative:移动自身时,参考自身的原来位置而移动,移动子元素(子元素设置了absolute ...
- Python之telnetlib模块
telnetlib是python标准库中的一员,我们可以使用该模块以telnet的方式与服务器交互.请观察下面示例了解它的用法: import telnetlib def run_telnet(hos ...
- Nginx版本平滑升级方案
背景:由于负载均衡测试服务器中nginx版本过低,存在安全漏洞,查询相关修复漏洞资料,需要采取nginx版本升级形式对漏洞进行修复. Nginx平滑升级方案 1.案例采用版本介绍 旧版本 nginx- ...
- SQL语句之高级使用
1.select top select top 用于规定要返回的数据的数目 注意:并非所有的数据库系统都支持 SELECT TOP 语句. MySQL 支持 LIMIT 语句来选取指定的条数数据, ...
- 使用Mosquitto实现MQTT客服端C语言
上一篇文章已经将mosquitto移植到了arm平台上,现在将使用mosquitto完成mqtt客服端的demo,了解过mqtt协议的小伙伴都知道,mqtt主要分为代理服务器.发布者.订阅者三部分 ...
- Appium自动化(11) - 详解 Applications 类里的方法和源码解析
如果你还想从头学起Appium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1693896.html 前言 Applications 类 ...
- JAVA安全基础之代理模式(一)
JAVA安全基础之代理模式(一) 代理模式是java的一种很常用的设计模式,理解代理模式,在我们进行java代码审计时候是非常有帮助的. 静态代理 代理,或者称为 Proxy ,简单理解就是事情我不用 ...
- C# 反射 + Quartz,实现流程处理
场景: 前不久,公司里项目经理要求我实现流程处理,比如,用户可以定义一个定时任务,每周一查看报表.定时任务很简单,用Quartz可以实现,但是用户自己选择报表就比较麻烦,因为系统的不同模块的生成报表的 ...
- String底层使用是char数组还是byte数组
结论:jdk1.8及以前String底层使用是char[],1.9开始使用byte[] jdk1.8 jdk13