Johnson 算法

全源最短路径求解其实是单源最短路径的推广,求解单源最短路径的两种算法时间复杂度分别为:

  • Dijkstra 单源最短路径算法:时间复杂度为 \(O(E + VlogV)\),要求权值非负;
  • Bellman-Ford 单源最短路径算法:时间复杂度为 \(O(VE)\),适用于带负权值情况;

如果对全图顶点遍历,使用 Dijkstra 算法,时间复杂度将变成 \(O(VE + V2logV)\),看起来优于 Floyd-Warshall 算法的 \(O(V3)\)。不过,Dijkstra 算法要求权值重不能为负。

Johnson 算法能调整权重为负的图,使之能够使用 Dijkstra 算法。

以下图为例,Johnson 算法对下图进行re-weight操作,使权重不为负,并且re-weight后,计算出来的最短路径仍然正确。

首先,新增一个源顶点 ,并使其与所有顶点连通,新边赋权值为 0,如下图所示。

接下来重新计算新增顶点到其它顶点的最短路径,利用单源最短路径算法,图中存在负权重节点,使用bellman ford算法,计算新增节点到其它节点的最短路径 h[],然后使用如下公式对所有边的权值进行 "re-weight":

w(u, v) = w(u, v) + (h[u] - h[v]).

对于此公式的证明请参考算法导论一书。

现在除新增结点外,其它结点的相关边权重值都已经为正数了,可以将新增结点删除,对其它结点使用Dijkstra 算法了。

例题

【模板】Johnson 全源最短路

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read(){
int x=0;bool f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
} void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(48+x%10);
} void writeln(int x){write(x);putchar('\n');}
void writebl(int x){write(x);putchar(' ');} #define I inline
#define R register const int maxn = 3e3+5;
const int maxm = maxn*6;
#define inf 1000000000 struct Johnson{
struct edge{int v,w,next;}e[maxm];
int head[maxn],vis[maxn],dis[maxn],tot,h[maxn];
void add(int u,int v,int w){e[++tot]=(edge){v,w,head[u]};head[u]=tot;}
struct node{
int dis;int pos;
bool operator < (const node &x)const{return x.dis<dis;}
};
priority_queue<node> q;
I void dijkstra(int s){
memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));
dis[s] = 0;
q.push((node){0,s});
while(!q.empty()){
node tmp = q.top();q.pop();
int x = tmp.pos,d = tmp.dis;
if(vis[x])continue;vis[x] = 1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(dis[v] > dis[x] + e[i].w){
dis[v] = dis[x] + e[i].w;
if(!vis[v])q.push((node){dis[v],v});
}
}
}
}
int tim[maxn];
I bool spfa(int s,int n){
queue<int> q;
memset(h,0x3f,sizeof(h));memset(vis,0,sizeof(vis));
h[s]=0;vis[s]=1;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
if(++tim[u]>n-1)return 0;
for(R int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(h[v]>h[u]+e[i].w){
h[v]=h[u]+e[i].w;
if(!vis[v])q.push(v),vis[v]=1;
}
}
}
return 1;
}
}J;
int n,m;
signed main(){
n=read(),m=read();
for(R int i=1,u,v,w;i<=m;++i){
u=read(),v=read(),w=read();
J.add(u,v,w);
}
for(R int i=1;i<=n;++i)J.add(0,i,0);
if(!J.spfa(0,n)){puts("-1");return 0;}
for(R int u=1;u<=n;++u)
for(R int i=J.head[u];i;i=J.e[i].next){
int v=J.e[i].v;
J.e[i].w+=J.h[u]-J.h[v];
}
for(R int i=1;i<=n;++i){
J.dijkstra(i);
long long ans=0;
for(R int j=1;j<=n;++j){
if(J.dis[j]==J.dis[n+1])ans+=j*inf;
else ans+=j*(J.dis[j]+J.h[j]-J.h[i]);
}
writeln(ans);
}
}

Johnson 最短路算法的更多相关文章

  1. Johnson算法:多源最短路算法

    Johnson算法 请不要轻易点击标题 一个可以在有负边的图上使用的多源最短路算法 时间复杂度\(O(n \cdot m \cdot log \ m+n \cdot m)\) 空间复杂度\(O(n+m ...

  2. Dijkstra 最短路算法(只能计算出一条最短路径,所有路径用dfs)

    上周我们介绍了神奇的只有五行的 Floyd 最短路算法,它可以方便的求得任意两点的最短路径,这称为"多源最短路".本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做&q ...

  3. Dijkstra最短路算法

    Dijkstra最短路算法 --转自啊哈磊[坐在马桶上看算法]算法7:Dijkstra最短路算法 上节我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最 ...

  4. Floyd最短路算法

    Floyd最短路算法 ----转自啊哈磊[坐在马桶上看算法]算法6:只有五行的Floyd最短路算法 暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计 ...

  5. Book 最短路算法

    用HDU2544整理一下最近学的最短路算法 1.Dijkstra算法 原理:集合S表示已经找到最短路径的点,d[]表示当前各点到源点的距离 初始时,集合里面只有源点,当每个点u进入集合S时,用d[u] ...

  6. 近十年one-to-one最短路算法研究整理【转】

    前言:针对单源最短路算法,目前最经典的思路即标号算法,以Dijkstra算法和Bellman-Ford算法为根本演进了各种优化技术和算法.针对复杂网络,传统的优化思路是在数据结构和双向搜索上做文章,或 ...

  7. 【啊哈!算法】算法7:Dijkstra最短路算法

    上周我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最短路”.本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径”.例如求下图 ...

  8. 【啊哈!算法】算法6:只有五行的Floyd最短路算法

            暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程.         上图中有 ...

  9. Comet OJ 热身赛(E题)(处理+最短路算法)

    dijkstra 已经提交 已经通过 42.86% Total Submission:189 Total Accepted:81 题目描述 Eagle Jump公司正在开发一款新的游戏.泷本一二三作为 ...

  10. 【最短路算法】Dijkstra+heap和SPFA的区别

    单源最短路问题(SSSP)常用的算法有Dijkstra,Bellman-Ford,这两个算法进行优化,就有了Dijkstra+heap.SPFA(Shortest Path Faster Algori ...

随机推荐

  1. KVM 硬盘分区扩容(GPT与MBR两种分区、fdisk 与 growpart两种方法)

    因为认知顺序的原因,之前我都是用fdisk命令手工删除分区表后重建进行扩容,后面才发现可以用growpart命令. 实战建议直接点 AWS EC2 存储空间扩容 跳转过去参考,学习操作可以继续往下看. ...

  2. 基于inotify-tools和rsync实现文件实时同步

    前言 系统:centos 7 inotify-tools版本:3.14(3.22版本测试有问题,所以找的比较旧的一版) 主机IP: 192.168.137.7(服务端,接收同步文件) 192.168. ...

  3. JavaScript的Map和WeakMap

    熟悉JavaScript的Map和WeakMap Map Map的键/值可以是任何类型 基本API 初始化映射: //使用new关键字和Map构造函数进行初始化 const m1 = new Map( ...

  4. 小版本更新kubernetes

    小版本更新kubernetes 背景 最近一段时间躺平了没有更新我的博客文档.感谢各位小伙伴一直以来的支持. 此脚本基于 https://github.com/cby-chen/Kubernetes/ ...

  5. Linux 之 shell 编程

    Linux 之 shell 编程学习笔记(并不完全正确,有误请指正) 概念性知识点 脚本概念 脚本(Script),是使用一种特定的描述性语言,依据一定的格式编写的 可执行文件 运行脚本要求 脚本须有 ...

  6. AI绘画:StableDiffusion制作AI赛博机车图保姆级教程

    本教程收集于:AIGC从入门到精通教程汇总 如果你具备Stable Diffusion的基础知识,那制作AI赛博机车图会更为顺畅.即便没有这个基础,只要严格按照教程的步骤执行,也能成功制作出来. 本教 ...

  7. Redis从入门到放弃(11):雪崩、击穿、穿透

    1.前言 Redis作为一款高性能的缓存数据库,为许多应用提供了快速的数据访问和存储能力.然而,在使用Redis时,我们不可避免地会面对一些常见的问题,如缓存雪崩.缓存穿透和缓存击穿.本文将深入探讨这 ...

  8. 如何分析 JVM 内存瓶颈浅谈

    背景: 当操作系统内存出现瓶颈时,我们便会重点排查那个应用占用内存过大.对于更深一步分析内存的使用,就进一步去了解内存结构,应用程序使用情况,以及内存如何分配.如何回收,这样你才能更好地确定内存的问题 ...

  9. 如何创建集成 LSP 支持多语言的 Web 代码编辑器

    对于一个云开发平台来说,一个好的 Web IDE 能很大程度地提高用户的编码体验,而一个 Web IDE 的一个重要组成部分就是代码编辑器. 目前有着多款 web 上的代码编辑器可供选择,比如 Ace ...

  10. 成本阶问题:财务模块axcr004合计金额检核表第18行合计金额与明细差异过大问题处理?

    财务模块axcr004合计金额检核表第18行合计金额与明细差异过大问题处理? 可能原因:生产开立工单时元件未建在生产料件BOM明细中,导致成本阶没有算到,需要手动更改成本阶. 公式: 处理办法:修改成 ...