一、问题引入

目前我们所知道的一些常见的最短路算法有 dijkstra、spfa、floyd。

dijkstra 和 spfa 是单源最短路,floyd 是多源最短路。

如果我们需要在 \(O(nm)\) 等级的时间复杂度下求出多源最短路,并且图存在负权,那么它就叉掉了这三种最短路算法,因为 dijkstra 无法处理负权,spfa 跑 \(n\) 次虽然一般跑不满,但是只要卡一下,就可以卡到 \(O(n^2m)\) 的时间复杂度,floyd 时间复杂度 \(O(n^3)\),也过不了,这个时候,就出现了 Johnson 算法,它是一种依靠 dijkstra 和 spfa 的算法。

二、Johnson 算法流程

  • 新建超级源点,给每个点连接一条边。
  • 算出每个点到超级源点的最短距离 \(h_i\),使用 spfa,因为此时存在负权。
  • 给每条边的 \(w_i\) 更新为 \(h_u-h_v+w_i\),\(u\) 和 \(v\) 为这条边连接的两个节点。
  • 计算最短路,使用 dijkstra,此时已经不存在负权。
  • 输出时要减去 \(h_j-h_i\)。

三、算法正确性证明

考虑如何将 dijkstra 优化成可以求负边权的算法。

首先有一种很容易想到的思路,就是将所有边都加上一个数,使得所有边的权值都变成非负整数,但是这种想法是错的,因为如果这样就会出现路径经过的边数不同导致最短路计算错误,所以我们需要使每个点加上一个数,使得任何一个最短路多余的值进行消掉之后只剩下开头点和结尾点,才能正确算出最短路。所以我们要将每一个点 \(i\) 设置一个值 \(h_i\),然后假设一条路径的边进行加工后,假设它的路径起点为 \(s\),终点为 \(t\),则路径为 \(s \rightarrow p_1 \rightarrow p_2 \rightarrow p_3 \rightarrow \dots \rightarrow p_k \rightarrow t\),其权值为 \(w(s,p_1)+h_s-h_{p_1}+w(p_1,p_2)+h_{p_1}-h_{p_2}+w(p_2,p_3)+h_{p_2}-h_{p_3}+\dots+w(p_k,t)+h_{p_k}-h_t\),消掉后变成了 \(w(s,p_1)+w(p_1,p_2)+w(p_2,p_3)+\dots+w(p_k,t)+h_s-h_t\),于是这样权值就和边的数量一点关系也没有了,目前已经证明了一半,那如何证明更新后的边权一定非负?首先对于任意一条边 \((u,v)\),它一定满足 \(h_v \le h_u+w(u,v)\),移项后得 \(0 \le h_u-h_v+w(u,v)\),则 \(h_u-h_v+w(u,v) \ge 0\),于是我们就证明了任意一条边更新后权值绝对非负。

四、例题

P5905 【模板】全源最短路(Johnson)

代码:

#include <bits/stdc++.h>
using namespace std;
namespace fast_IO {
#define FASTIO
#define IOSIZE 100000
char ibuf[IOSIZE], obuf[IOSIZE];
char *p1 = ibuf, *p2 = ibuf, *p3 = obuf;
#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,IOSIZE,stdin),p1==p2)?(EOF):(*p1++))
#define putchar(x) ((p3==obuf+IOSIZE)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
#endif//fread in OJ, stdio in local #define isdigit(ch) (ch>47&&ch<58)
#define isspace(ch) (ch<33)
template<typename T> inline T read() {
T s = 0;
int w = 1;
char ch;
while (ch = getchar(), !isdigit(ch) and (ch != EOF)) if (ch == '-') w = -1;
if (ch == EOF) return false;
while (isdigit(ch)) s = s * 10 + ch - 48, ch = getchar();
return s * w;
}
template<typename T> inline bool read(T &s) {
s = 0;
int w = 1;
char ch;
while (ch = getchar(), !isdigit(ch) and (ch != EOF)) if (ch == '-') w = -1;
if (ch == EOF) return false;
while (isdigit(ch)) s = s * 10 + ch - 48, ch = getchar();
return s *= w, true;
}
inline bool read(char &s) {
while (s = getchar(), isspace(s));
return true;
}
inline bool read(char *s) {
char ch;
while (ch = getchar(), isspace(ch));
if (ch == EOF) return false;
while (!isspace(ch)) *s++ = ch, ch = getchar();
*s = '\000';
return true;
}
template<typename T> inline void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
inline void print(char x) {
putchar(x);
}
inline void print(char *x) {
while (*x) putchar(*x++);
}
inline void print(const char *x) {
for (int i = 0; x[i]; i++) putchar(x[i]);
}
#ifdef _GLIBCXX_STRING
inline bool read(std::string& s) {
s = "";
char ch;
while (ch = getchar(), isspace(ch));
if (ch == EOF) return false;
while (!isspace(ch)) s += ch, ch = getchar();
return true;
}
inline void print(std::string x) {
for (int i = 0, n = x.size(); i < n; i++)
putchar(x[i]);
}
#endif//string
template<typename T, typename... T1> inline int read(T& a, T1&... other) {
return read(a) + read(other...);
}
template<typename T, typename... T1> inline void print(T a, T1... other) {
print(a);
print(other...);
} struct Fast_IO {
~Fast_IO() {
fwrite(obuf, p3 - obuf, 1, stdout);
}
} io;
template<typename T> Fast_IO& operator >> (Fast_IO &io, T &b) {
return read(b), io;
}
template<typename T> Fast_IO& operator << (Fast_IO &io, T b) {
return print(b), io;
}
#define cout io
#define cin io
#define endl '\n'
}
using namespace fast_IO;
const int N = 3e3+5;
struct node
{
int x;
int w;
int operator<(const node&a)const
{
return w>a.w;
}
};
vector<node>a[N];
int vis[N];
long long h[N];
long long d[N];
int t[N];
signed main()
{
int n,m;
cin >> n >> m;
for(int i = 1;i<=m;i++)
{
int x,y,w;
cin >> x >> y >> w;
a[x].push_back({y,w});
}
for(int i = 1;i<=n;i++)
{
a[0].push_back({i,0});
}
memset(h,0x3f,sizeof(h));
queue<int>q;
q.push(0);
vis[0] = 1;
h[0] = 0;
while(q.size())
{
int x = q.front();
q.pop();
vis[x] = 0;
for(int i = 0;i<a[x].size();i++)
{
int v = a[x][i].x;
int w = a[x][i].w;
if(h[v]>h[x]+w)
{
h[v] = h[x]+w;
if(!vis[v])
{
vis[v] = 1;
q.push(v);
t[v]++;
if(t[v] == n+1)
{
printf("-1");
return 0;
}
}
}
}
}
for(int i = 1;i<=n;i++)
{
for(int j = 0;j<a[i].size();j++)
{
int v = a[i][j].x;
a[i][j].w+=h[i]-h[v];
}
}
for(int i = 1;i<=n;i++)
{
priority_queue<node>q;
q.push({i,0});
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[i] = 0;
while(q.size())
{
int x = q.top().x;
q.pop();
if(vis[x])
{
continue;
}
vis[x] = 1;
for(int i = 0;i<a[x].size();i++)
{
int v = a[x][i].x;
int w = a[x][i].w;
if(d[v]>d[x]+w)
{
d[v] = d[x]+w;
q.push({v,d[v]});
}
}
}
long long sum = 0;
for(int j = 1;j<=n;j++)
{
if(d[j] == d[0])
{
sum+=(long long)j*(long long)1000000000;
}
else
{
sum+=j*(d[j]+h[j]-h[i]);
}
}
cout << sum << "\n";
}
return 0;
}

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

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

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

  2. 模板C++ 03图论算法 2最短路之全源最短路(Floyd)

    3.2最短路之全源最短路(Floyd) 这个算法用于求所有点对的最短距离.比调用n次SPFA的优点在于代码简单,时间复杂度为O(n^3).[无法计算含有负环的图] 依次扫描每一点(k),并以该点作为中 ...

  3. Johnson全源最短路

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

  4. Johnson 全源最短路

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

  5. 单源最短路——Bellman-Ford算法

    1.Dijkstra的局限性 Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的. 列如以 ...

  6. 单源最短路dijkstra算法&&优化史

    一下午都在学最短路dijkstra算法,总算是优化到了我能达到的水平的最快水准,然后列举一下我的优化历史,顺便总结总结 最朴素算法: 邻接矩阵存边+贪心||dp思想,几乎纯暴力,luoguTLE+ML ...

  7. 单源最短路——dijkstra算法

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 问 ...

  8. 图论-单源最短路-SPFA算法

    有关概念: 最短路问题:若在图中的每一条边都有对应的权值,求从一点到另一点之间权值和最小的路径 SPFA算法的功能是求固定起点到图中其余各点的的最短路(单源最短路径) 约定:图中不存在负权环,用邻接表 ...

  9. 多源最短路Floyd 算法————matlab实现

    弗洛伊德(Floyd)算法是一种用于寻找给定的加权图中顶点间最短路径的算法.该算法名称以创始人之一.1978年图灵奖获得者.斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名. 基本思想 通过Floyd计 ...

  10. 单源最短路Dijkstra算法——matlab实现

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijk ...

随机推荐

  1. COS 数据工作流 + Serverless云函数自定义处理能力发布!

    01 背景 在工业4.0的浪潮下,智能和数据与物理世界结合越加紧密,多元化.灵活.高效的数据处理能力成为各行各业的热点需求. ​ 虽然COS已经预置电商.文创.教育.社交.安防等行业需要的基础数据处理 ...

  2. Flutter自动生成安卓和IOS图标

    flutter_launcher_icons 用这个库自动生成 首先添加依赖 dev_dependencies: flutter_launcher_icons: flutter_icons: andr ...

  3. HttpClientFactory in ASP.NET Core 2.1 Part 1 介绍

    HttpClientFactory in ASP.NET Core 2.1 Part 1 原文地址:https://www.stevejgordon.co.uk/introduction-to-htt ...

  4. 非root用户使用AntDeploy部署docker

    AntDeploy这个东西非常好用,可以直接将.NET CORE的程序直接发布到docker,刚好我有这个需求,但是程序默认给的账户示例是root账户的,需要对于需要分散开发的同学来说,这个东西风险有 ...

  5. Shiro安全框架【认证】+【授权】

    1.Shiro的核心架构 Subject:程序主体 Security Manager:安全管理器 Authentication:做认证 Authorizer:做授权 Session Manager:会 ...

  6. 【PHP】读取本地文件夹中所有图片并显示

    <? //获取文件夹下的所有文件 $dir_str = ''; $imgType = array('gif','png','jpg','jpeg','bmp'); $handle = opend ...

  7. 【web】Ajax Study Note

    1.Create a XMLHttpRequest Object (1)For IE7+.Firefox.Chrome.Safari and Opera variable = new XMLHttpR ...

  8. kubectl get deploy

    for i in `kubectl get deployments.apps -n nvpc-apps-02|grep -v NAME|awk '{print $1}'`; do kubectl ge ...

  9. Qt编写安防视频监控系统51-功能激活

    一.前言 随着视频监控系统本身功能的增多,以及用户定制功能的增多(比如视频监控系统摇身一变成了机器人监控.无人机监控.挖掘机监控等),除了提供工作模式这个切换开关,还需要对不同的工作模式启用禁用不同的 ...

  10. [转]C# PowerPoint操作的基本用法

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using OFFICECO ...