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个猜想:
  1. 对每条边进行相同的增量,使所有边非负。
  2. 改变图的结构,不改变图的性质,使Dijkstra可做。
  3. 对每条边单独设计增量,使图的性质保证并且全图非负。

可以看出只有第三种是正确的。

为什么第一种不正确?

如上图(箭头错,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 $

\[= w_{1,2} + w_{2,3} + ⋯ + w _{t−1,t} + (dis u_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 全源最短路径算法学习笔记的更多相关文章

  1. Johnson 全源最短路径算法

    解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: ...

  2. Floyd-Warshall 全源最短路径算法

    Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E) 上每对顶点间的最短路径问题,即全源最短路径问题(All-Pairs Shortest Paths Probl ...

  3. 【学习笔记】 Johnson 全源最短路

    前置扯淡 一年多前学的最短路,当时就会了几个名词的拼写,啥也没想过 几个月之前,听说了"全源最短路"这个东西,当时也没说学一下,现在补一下(感觉实在是没啥用) 介绍 由于\(spf ...

  4. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  5. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  6. Johnson全源最短路

    例题:P5905 [模板]Johnson 全源最短路 首先考虑求全源最短路的几种方法: Floyd:时间复杂度\(O(n^3)\),可以处理负权边,但不能处理负环,而且速度很慢. Bellman-Fo ...

  7. Johnson 全源最短路

    学这个是为了支持在带负权值的图上跑 Dijkstra. 为了这个我们要考虑把负的权值搞正. 那么先把我们先人已经得到的结论摆出来.我们考虑先用 SPFA 对着一个满足三角形不等式的图跑一次最短路,具体 ...

  8. Dijkstra 单源最短路径算法

    Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...

  9. Bellman-Ford 单源最短路径算法

    Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法.该算法由 Richard Bellman 和 Leste ...

随机推荐

  1. Win10 下安装Ubuntu 21.04桌面版 双系统 并设置win10为默认启动系统 详细教程

    @ 目录 〇.写在前面 〇 - Plus:如何进入BIOS 〇 - Plus - Plus:U盘启动快捷键 一.磁盘分区:Win10划分未分配空间 二.下载Ubuntu 21.04镜像 三.安装U盘启 ...

  2. Docker下制作一个容器镜像

    操作过程描述: (1)先基于centos的镜像启动一个centos容器 (2)在这个容器中安装nginx (3)然后把这个已经安装了nginx的容器制作成一个docker的镜像 操作:docker c ...

  3. 如何在线安全清理mysql慢日志

    部门mysql数据库的服务器磁盘空间快满了,查看了下磁盘的慢日志,发现占了50多个g,百度了资料后删除成功,现在和大家分享下我清理的过程,或许有更好的办法. 1.首先连接进入mysql,查看慢日志的状 ...

  4. MySQL-技术专题-MySQL主从架构以及[半同步机制]模式大全

    MySQL的主从复制 一般在大规模的项目上,都是使用MySQL的复制功能来创建MySQL的主从集群的. 主要是可以通过为数据库服务器配置一个或多个备库的方式来进行数据同步. 复制的功能不仅有利于构建高 ...

  5. 使用Git上传项目到GitHub仓库

    GitHub账号注册很长时间了,但是没怎么上传过东西.今天学习如何利用Git工具将代码上传到GitHub,了解了一些常用命令 $ git config --global user.name " ...

  6. Jetpack Compose学习(5)——从登录页美化开始学习布局组件使用

    原文:Jetpack Compose学习(5)--从登录页美化开始学习布局组件使用 | Stars-One的杂货小窝 本篇主要讲解常用的布局,会与原生Android的布局控件进行对比说明,请确保了解A ...

  7. 试玩Aid Learning

    前言 记录一下步骤 下载安装 github官网 切换源 ## 打开Terminal复制回车即可 cd /etc/apt/&& cp sources.list sources.list. ...

  8. windows安装python2.7、python3.7和pycharm

    下载安装包 下载可执行文件 安装 安装2.7 安装pycharm

  9. phpspider PHP 爬虫

    * 通过composer下载 composer require owner888/phpspider // composer.json { "require": { "o ...

  10. Lucene基础入门

    1. 数据的分类 结构化数据: 查询方法     数据库 非结构化数据: 查询方法  : (1)顺序扫描法   : 一行一行的看,从头看到尾 (2)全文检索 : 将一部分信息提取出来,重新组织将其变得 ...