碎碎念

学完Johnson已经好久了但一直没有时间总结,今天终于有时间了来写一下

其实这个算法还是比较简单的,刚学完最短路的小蒟蒻也可以学会

求点赞 + 评论qwq,支持一下小蒟蒻吧OvO


例题:

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

大意:我们需要在 \(O(nm)\) 的时间复杂度下处理带负权图的全源最短路

我们熟知的最短路算法有SPFA、dijstra和floyd,但是他们都无法达到优秀的复杂度

  • SPFA虽然理论上是跑不满的,但是稍加卡卡就会被卡到 \(O(n ^ 2 m)\)。

  • dijstra处理不了带负权的图

  • floyd复杂度为 \(O(n ^ 3)\) 也通过不了本题

这时候有人就要问了,我们直接给每条边的边权加上一个固定值,使每个边权都不为负数不就可以了

但这是错误的,我们需要加上特定的一个数才可以保证答案不会变

我有一个绝妙的证明,但这里空白太小,我写不下

咳咳其实我不会证


正片开始

我们还是一样的思路,将负边权全部转化为正数。

那么我们怎么求出“特定的数”呢?

这就是 Johnson 的核心思想了

算法过程

  • 新建一个点,并对每一个点都连上边

  • 利用 SPFA 算出每个点到刚刚新建的点的最短距离,因为还存在负边权,所以不能使用dijstra

  • 给每一条边的边权更新为这条边连接的两个节点的离新建节点最短路差值并加上原来的边权

  • 这个时候已经没有负边权了,直接使用dijstra计算最短路

  • 因为我们之前加上了一个 两个节点的离新建节点最短路的差值 所以输出时要减去这个结果

注意:dijstra最好使用堆优化,这样时间复杂度就为 \(O(nlogn)\)

总时间复杂度为 \(O(nm log m)\)

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, vis[5005], h[5005], dis[5005], f[5005];
struct node {
int cnt, head[5005], nxt[10005], to[10005], data[10005];
void add(int x, int y, int w) {
nxt[++ cnt] = head[x];
head[x] = cnt;
to[cnt] = y;
data[cnt] = w;
}
}qwq;
bool SPFA(int x) {
queue<int> q;
for(int i = 1;i <= n;i ++) {
h[i] = 1e9, f[i] = 0;
}
h[x] = 0, f[x] = true;
q.push(x);
while(!q.empty()) {
int xx = q.front();
q.pop();
f[xx] = 0;
for(int i = qwq.head[xx];i != 0;i = qwq.nxt[i]) {
if(h[qwq.to[i]] > h[xx] + qwq.data[i]) {
h[qwq.to[i]] = h[xx] + qwq.data[i];
if(!f[qwq.to[i]]) {
if(++ vis[qwq.to[i]] >= n + 1) {
return 0;
}
f[qwq.to[i]] = 1;
q.push(qwq.to[i]);
}
}
}
}
return 1;
}
void dijstra(int x) {
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
for(int i = 1;i <= n;i ++) {
dis[i] = 1e9;
f[i] = 0;
}
q.push({0, x});
dis[x] = 0;
while(!q.empty()) {
int xx = q.top().second;
q.pop();
if(f[xx] == 0) {
f[xx] = 1;
for(int i = qwq.head[xx];i != 0;i = qwq.nxt[i]) {
if(dis[qwq.to[i]] > dis[xx] + qwq.data[i]) {
dis[qwq.to[i]] = dis[xx] + qwq.data[i];
if(f[qwq.to[i]] == 0) {
q.push({dis[qwq.to[i]], qwq.to[i]});
}
}
}
}
}
}
signed main() {
cin >> n >> m;
for(int i = 1;i <= m;i ++) {
int u, v, w;
cin >> u >> v >> w;
qwq.add(u, v, w);
}
for(int i = 1;i <= n;i ++) {
qwq.add(0, i, 0);
}
if(!SPFA(0)) {
cout << -1;
return 0;
}
for(int u = 1;u <= n;u ++) {
for(int i = qwq.head[u];i != 0;i = qwq.nxt[i]) {
qwq.data[i] += h[u] - h[qwq.to[i]];
}
}
for(int i = 1;i <= n;i ++) {
dijstra(i);
int ans = 0;
for(int j = 1;j <= n;j ++) {
if(dis[j] == 1e9) ans += j * 1e9;
else ans += j * (dis[j] + (h[j] - h[i]));
}
cout << ans << endl;
}
}

全源最短路Johnson学习笔记的更多相关文章

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

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

  2. Johnson全源最短路

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

  3. Johnson 全源最短路

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

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

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

  5. Python全栈工程师之html学习笔记

    https://www.bilibili.com/video/av15241731 笔记来源:黑马程序员 HTML(Hyper Text Markup Language):超文本标签语言 HTML标签 ...

  6. zepto源码--定义变量--学习笔记

    主要了解一下zepto定义的初始变量. 逐一以自己的理解解析,待到后面完全透彻理解之后,争取再写一遍zepto源码学习的文章. 其中的undefined确实不明白为什么定义这么个变量在这里. docu ...

  7. zepto源码--整体框架--学习笔记

    为了深入学习javascript,根据别人推荐的方法之一:研究源码. 相对而言,之前的项目中仅仅使用过zepto和jquery,当前阶段,看到好几千行的jquery源码,心生敬畏,望而却步,所以选择相 ...

  8. 《Android源码设计模式》学习笔记之ImageLoader

    微信公众号:CodingAndroid cnblog:http://www.cnblogs.com/angel88/ CSDN:http://blog.csdn.net/xinpengfei521 需 ...

  9. 《PHP7底层设计与源码实现》学习笔记1——PHP7的新特性和源码结构

    <PHP7底层设计与源码实现>一书的作者陈雷亲自给我们授课,大佬现身!但也因此深感自己基础薄弱,遂买了此书.希望看完这本书后,能让我对PHP7底层的认识更上一层楼.好了,言归正传,本书共1 ...

  10. 阅读{django-restframework}源码[generics.py]学习笔记

    首先django-restframework是基于django的一个框架.   mixins.py中开头是这样写的: Basic building blocks for generic class b ...

随机推荐

  1. Spring扩展接口-BeanFactoryAware

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  2. redis的常见面试题

    为什么要用redis 减少了mysql数据库的压力, 在这之前mysql一个人承受,然后要承受大量的数据请求, 大部分都是读操作.而且经常都是重复查一个东西,浪费了很多时间进行磁盘io redis将数 ...

  3. sublime text 2 snippet 设置

    1.标准文件写法 <snippet> <content><![CDATA[ 你需要插入的代码片段${1:name} ]]></content> < ...

  4. Centos下多种PHP拓展安装方法

    http://my.oschina.net/u/2400083/blog/518195

  5. Kafka入门实战教程(3).NET Core操作Kafka

    1 可用的Kafka .NET客户端 作为一个.NET Developer,自然想要在.NET项目中集成Kafka实现发布订阅功能.那么,目前可用的Kafka客户端有哪些呢? 目前.NET圈子主流使用 ...

  6. MongoDB入门实战教程(6)

    本系列教程目录: MongoDB入门实战教程(1) MongoDB入门实战教程(2) MongoDB入门实战教程(3) MongoDB入门实战教程(4) MongoDB入门实战教程(5) 通过前面几篇 ...

  7. YOXO_ERP管理系统怎么操作?

    YOXO_ERP管理系统怎么操作? YOXO_ERP系统是由订单管理 .售后管理(客户管理).入账管理.未到款发货.入库.付款.成品出库.库存管理.供应商管理八大板块组成的一个集仓管.客货.发货.供应 ...

  8. 关于C++变量类型精度的研究

    今天在练习的时候,忽然发现了一个神奇的"bug",就是C++在进行数据精度处理的时候会有不同的结果,下面便是我做的两个小测试. NO1. 如图,在同时使用int类型的数据进行除法操 ...

  9. mysql 笛卡尔积

    简介 新生成的表的行数 第一个表的行数 * 第二个表的行数 参考链接 mysql 必知必会

  10. hot_driver's 评语

    1)没有学会用面向对象的思维去分析和设计,比如:当我们要抽象出一个LED类,那么我们应该定义它的属性和操作.而我看到LED只有属性,而没有操作.常见的操作是关灯.开灯.调整亮度.调整颜色:而这些操作被 ...