Johnson

理论

全源最短路算法

  1. Floyd 算法,时间复杂度为 O(n³)
  2. 跑 n 次 Bellman - Ford 算法,时间复杂度是 O(n²m)
  3. 跑 n 次 Heap - Dijkstra 算法,时间复杂度是 O(nmlogm)
    第 3 种算法被 Johnson 做了改造,可以求解带负权边的全源最短路。

Johnson(约翰逊)算法
4. 新建一个虚拟源点 0,从该点向其他所有点连一条边权为 0 的边,再用spfa 算法求出从 0 号点到其他所有点的最短路 h(i)。
5. 将新图的边权改造为 w(u,v) + h(u) - h(v),这样能确保边权非负。
6. 以每个点为起点,跑 n 轮 Heap - Dijkstra 算法,求出任意两点间最短路。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/900fec77653a4d379bc6fdad4e0f378a.pn

  1. 以下是结合Johnson算法对这幅图的分析:
    1. 构建虚拟源点及初始操作
    图中节点0是Johnson算法里新添加的虚拟源点 ,从它向节点1、2、3、4分别连了边权为0的边(绿色边)。接下来要用SPFA算法求从0号虚拟源点到其他所有节点的最短路 h ( i ) h(i) h(i) 。比如从节点0到节点1的边权为0 ,如果不存在更短路径, h ( 1 ) h(1) h(1) 初始就是0 ;从节点0到节点4边权为0 ,若没有其他路径干扰, h ( 4 ) h(4) h(4) 也为0 ;从节点0到节点2边权为-5 ,0 ->3->2。图中绿色数字即为初始操作(SPFA)后虚拟节点到个其余各个点的距离。
    2. 边权改造
    根据Johnson算法,需要将图中各边的边权改造为 w ( u , v ) + h ( u ) − h ( v ) w(u, v)+h(u) - h(v) w(u,v)+h(u)h(v) ,以此确保边权非负。例如,对于节点1到节点2这条边,原始边权 w ( 1 , 2 ) w(1, 2) w(1,2) 为-1 , h ( 1 ) = 0 h(1)=0 h(1)=0 h ( 2 ) = − − 5 h(2)= - -5 h(2)=5 ,则新边权为 0 + − 1 − ( − 5 ) = 4 0 + -1 - (-5)=4 0+1(5)=4 。途中红色数字为改造后的边权,黑色数字为改造前的边权。
    3. 计算全源最短路
    完成边权改造后,以每个点为起点,运行n轮Heap - Dijkstra算法(n为图中节点数,这里n = 4 ,不包含虚拟源点0 ),从而求出图中任意两点间的最短路。

各个最短路算法分析:

BFSHeap-DijkstraSPFAFloydJohnson
最短路类型最少步数单源单源全源全源
建图邻接矩阵邻接表邻接表邻接矩阵邻接表
算法贪心,松弛贪心,松弛插点法贪心,松弛
优化队列优先队列队列优先队列
负边权不能
判负环不能
时间复杂度O(n + m)O(mlogm)O(nm)O(n³)O(nmlogm)
图的大小大/中
n=10⁷, m=10⁷
大/中
n=10⁶, m=10⁶
中/小
n=10³, m=10⁴

n=10²
中/小
n=10³, m=10³

例题

https://www.luogu.com.cn/problem/P5905

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 1e9;
// 定义边的结构体,包含目标节点 v 和边的权重 w
struct edge { int v, w; };
// 邻接表存储图,e[i] 存储从节点 i 出发的所有边
vector<edge> e[N];
// vis 数组用于标记节点是否在队列中,cnt 数组用于记录每个节点入队的次数
int vis[N], cnt[N];
// h 数组用于存储从虚拟源点到各节点的最短路,d 数组用于存储从某个源点到各节点的最短路
long long h[N], d[N];
int n, m;// 使用 SPFA 算法计算从虚拟源点 0 到其他所有节点的最短路
void spfa() {// 定义一个队列用于存储待处理的节点queue<int> q;// 初始化 h 数组为一个很大的值,memset(63) 相当于将每个元素初始化为一个较大的数memset(h, 63, sizeof h);memset(vis, false, sizeof vis);// 虚拟源点到自身的距离为 0h[0] = 0;// 标记虚拟源点在队列中vis[0] = 1;// 将虚拟源点加入队列q.push(0);while (q.size()) {int u = q.front();q.pop();// 标记该节点不在队列中vis[u] = 0;// 遍历从节点 u 出发的所有边for (auto ed : e[u]) {// 获取目标节点 v 和边的权重 wint v = ed.v, w = ed.w;// 如果通过节点 u 到达节点 v 的距离更短,则更新 h[v]if (h[v] > h[u] + w) {h[v] = h[u] + w;// 更新节点 v 的入队次数cnt[v] = cnt[u] + 1;// 如果某个节点入队次数超过 n 次,说明存在负环if (cnt[v] > n) {printf("-1\n");// 存在负环,程序退出exit(0);}// 如果节点 v 不在队列中,则将其加入队列if (!vis[v]) {q.push(v);vis[v] = 1;}}}}
}// 使用 Dijkstra 算法计算从源点 s 到其他所有节点的最短路
void dijkstra(int s) {// 定义一个优先队列,用于存储待处理的节点,按照距离从小到大排序priority_queue<pair<long long, int>> q;// 初始化 d 数组为无穷大for (int i = 1; i <= n; i++) {d[i] = INF;}// 初始化 vis 数组为 false,表示所有节点都未被处理memset(vis, false, sizeof vis);// 源点到自身的距离为 0d[s] = 0;// 将源点加入优先队列q.push({ 0,s });// 当优先队列不为空时进行处理while (q.size()) {// 取出队首节点int u = q.top().second;q.pop();// 如果该节点已经被处理过,则跳过if (vis[u]) {continue;}// 标记该节点已被处理vis[u] = 1;// 遍历从节点 u 出发的所有边for (auto ed : e[u]) {// 获取目标节点 v 和边的权重 wint v = ed.v;int w = ed.w;// 如果通过节点 u 到达节点 v 的距离更短,则更新 d[v]if (d[v] > d[u] + w) {d[v] = d[u] + w;// 如果节点 v 未被处理,则将其加入优先队列if (!vis[v]) {q.push({ -d[v],v });}}}}
}int main() {cin >> n >> m;// 输入每条边的信息,并将其加入邻接表for (int i = 0; i < m; i++) {int a, b, c;cin >> a >> b >> c;e[a].push_back({ b,c });}// 从虚拟源点 0 向其他所有节点添加一条边权为 0 的边for (int i = 1; i <= n; i++) {e[0].push_back({ i,0 });//加虚拟边}// 调用 SPFA 算法计算从虚拟源点到其他所有节点的最短路spfa();// 对图的边权进行重新计算,确保所有边权非负for (int u = 1; u <= n; u++) {for (auto& ed : e[u]) {ed.w += h[u] - h[ed.v];//构造新边}}// 以每个节点为源点,调用 Dijkstra 算法计算最短路for (int i = 1; i <= n; i++) {dijkstra(i);long long ans = 0;// 计算从源点 i 到其他所有节点的最短路的加权和for (int j = 1; j <= n; j++) {// 如果节点 j 不可达,则使用无穷大计算if (d[j] == INF) {ans += (long long)j * INF;}// 否则,使用实际距离计算else {ans += (long long)j * (d[j] + h[j] - h[i]);}}// 输出结果printf("%lld\n", ans);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/77047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Exce格式化批处理工具详解:高效处理,让数据更干净!

Exce格式化批处理工具详解&#xff1a;高效处理&#xff0c;让数据更干净&#xff01; 1. 概述 在数据分析、报表整理、数据库管理等工作中&#xff0c;数据清洗是不可或缺的一步。原始Excel数据常常存在格式不统一、空值、重复数据等问题&#xff0c;影响数据的准确性和可用…

(三十七)Dart 中使用 Pub 包管理系统与 HTTP 请求教程

Dart 中使用 Pub 包管理系统与 HTTP 请求教程 Pub 包管理系统简介 Pub 是 Dart 和 Flutter 的包管理系统&#xff0c;用于管理项目的依赖。通过 Pub&#xff0c;开发者可以轻松地添加、更新和管理第三方库。 使用 Pub 包管理系统 1. 找到需要的库 访问以下网址&#xff0c…

代码随想录算法训练营第三十五天 | 416.分割等和子集

416. 分割等和子集 题目链接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划之背包问题&#xff0c;这个包能装满吗&#xff1f;| LeetCode&#xff1a;416.分割等和子集_哔哩哔哩_bilibi…

HTTP 教程 : 从 0 到 1 全面指南 教程【全文三万字保姆级详细讲解】

目录 HTTP 的请求-响应 HTTP 方法 HTTP 状态码 HTTP 版本 安全性 HTTP/HTTPS 简介 HTTP HTTPS HTTP 工作原理 HTTPS 作用 HTTP 与 HTTPS 区别 HTTP 消息结构 客户端请求消息 服务器响应消息 实例 HTTP 请求方法 各个版本定义的请求方法 HTTP/1.0 HTTP/1.1 …

spring功能汇总

1.创建一个dao接口&#xff0c;实现类&#xff1b;service接口&#xff0c;实现类并且service里用new创建对象方式调用dao的方法 2.使用spring分别获取dao和service对象(IOC) 注意 2中的service里面获取dao的对象方式不用new的(DI) 运行测试&#xff1a; 使用1的方式创建servic…

Vue.js 实现下载模板和导入模板、数据比对功能核心实现。

在前端开发中&#xff0c;数据比对是一个常见需求&#xff0c;尤其在资产管理等场景中。本文将基于 Vue.js 和 Element UI&#xff0c;通过一个简化的代码示例&#xff0c;展示如何实现“新建比对”和“开始比对”功能的核心部分。 一、功能简介 我们将聚焦两个核心功能&…

volatile关键字用途说明

volatile 关键字在 C# 中用于指示编译器和运行时系统&#xff0c;某个字段可能会被多个线程同时访问&#xff0c;并且该字段的读写操作不应被优化&#xff08;例如缓存到寄存器或重排序&#xff09;&#xff0c;以确保所有线程都能看到最新的值。这使得 volatile 成为一种轻量级…

【区块链安全 | 第三十五篇】溢出漏洞

文章目录 溢出上溢示例溢出漏洞溢出示例漏洞代码代码审计1. deposit 函数2. increaseLockTime 函数 攻击代码攻击过程总结修复建议审计思路 溢出 算术溢出&#xff08;Arithmetic Overflow&#xff09;&#xff0c;简称溢出&#xff08;Overflow&#xff09;&#xff0c;通常分…

百度的deepseek与硅基模型的差距。

问题&#xff1a; 已经下载速度8兆每秒&#xff0c;请问下载30G的文件需要多长时间&#xff1f; 关于这个问题。百度的回答如下&#xff1a; ‌30GB文件下载时间计算‌ ‌理论计算‌&#xff08;基于十进制单位&#xff09;&#xff1a; ‌单位换算‌ 文件大小&#xff1a;3…

车载诊断架构 --- 特殊定义NRC处理原理

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

面试题ing

1、js中set和map的作用和区别? 在 JavaScript 中&#xff0c;Set 和 Map 是两种非常重要的集合类型 1、Set 是一种集合数据结构&#xff0c;用于存储唯一值。它类似于数组&#xff0c;但成员的值都是唯一的&#xff0c;没有重复的值。Set 中的值只能是唯一的&#xff0c;任何…

Python爬虫第6节-requests库的基本用法

目录 前言 一、准备工作 二、实例引入 三、GET请求 3.1 基本示例 3.2 抓取网页 3.3 抓取二进制数据 3.4 添加headers 四、POST请求 五、响应 前言 前面我们学习了urllib的基础使用方法。不过&#xff0c;urllib在实际应用中存在一些不便之处。以网页验证和Cookies处理…

Go 学习笔记 · 进阶篇 · 第一天:接口与多态

&#x1f436;Go接口与多态&#xff1a;继承没了&#xff0c;但自由炸裂&#xff01; 最近翻 Go 的代码&#xff0c;突然看到这么一段&#xff1a; type Animal interface {Speak() string }我一愣&#xff0c;咦&#xff1f;这不就是 Java 里常见的“接口”吗&#xff1f; …

信息学奥赛一本通 1929:【04NOIP普及组】火星人 | 洛谷 P1088 [NOIP 2004 普及组] 火星人

【题目链接】 ybt 1929&#xff1a;【04NOIP普及组】火星人 洛谷 P1088 [NOIP 2004 普及组] 火星人 【题目考点】 1. 深搜回溯 2. STL next_permutation函数 头文件<algorithm> 函数定义&#xff1a;next_permutation(lb, ub, cmp) lb&#xff1a;区间下界&#xff…

借助 AI 工具使用 Python 实现北京市店铺分布地理信息可视化教程

一、项目概述 本项目通过 Python 的pyecharts库&#xff0c;结合 AI 工具辅助代码编写与逻辑梳理&#xff0c;实现北京市店铺数量分布及区域连线的地理信息可视化&#xff0c;最终生成交互式地图图表。 二、准备工作 1. 环境与工具 Python 环境&#xff1a;确保已安装 Pyth…

Python项目打包指南:PyInstaller与SeleniumWire的兼容性挑战及解决方案

前言 前段时间做一个内网开发的需求&#xff0c;要求将selenium程序打包成.exe放在内网的win7上运行&#xff0c;在掘金搜了一圈也没有发现相关文章&#xff0c;因此将过程中踩到的坑记录分享一下。 本文涵盖了具体打包操作、不同模块和依赖项的兼容性解决方案&#xff0c;以…

(一)栈结构、队列结构

01-线性结构-数组-栈结构 线性结构&#xff08;Linear List)是由n&#xff08;n>0)个数据元素&#xff08;结点&#xff09; a[0], a[1], a[2], a[3],...,a[n-1]组成的有限序列 数组 通常数组的内存是连续的&#xff0c;所以在知道数组下标的情况下&#xff0c;访问效率是…

【学Rust写CAD】35 alpha_mul_256(alpha256.rs补充方法)

源码 // Calculates (value * alpha256) / 255 in range [0,256], // for [0,255] value and [0,256] alpha256. pub fn alpha_mul_256(self,value: u32) -> Alpha256 {let prod value * self.0;Alpha256((prod (prod >> 8)) >> 8) }代码分析 这个函数 alph…

C# 与 相机连接

一、通过组件连接相机 需要提前在VisionPro里面保存一个CogAcqFifoTool相机工具为 .vpp 定义一个相机工具 CogAcqFifoTool mAcq null;将保存的相机工具放入mAcq中 string path “C:\Acq.vpp”; mAcq (CogAcqFifoTool)CogSerializer.LoadObjectFrommFile(path);给窗口相机…

Java并发编程高频面试题

一、基础概念 1. 并行与并发的区别&#xff1f; 并行&#xff1a;多个任务在多个CPU核心上同时执行&#xff08;物理上同时&#xff09;。并发&#xff1a;多个任务在单CPU核心上交替执行&#xff08;逻辑上同时&#xff09;。类比&#xff1a;并行是多个窗口同时服务&#x…