Part -1 前置算法
你需了解dijkstra算法与bellman_ford算法。
如果你想了解dijkstra,你可以去看程序员小灰的文章,C++代码我会在文章末尾给出(dijkstra,floyd,bellmen_ford和主角johnson)
如果你想了解bellman_ford,可以参考《算法笔记》中的bellman_ford讲解,或者百度一下。
Part 1 起因
nlogn的堆优化dijkstra跑n次也要比n^3的floyd快。用dijkstra跑全源最短路应该不是一个坏主意。于是我询问了学长:
询问得知,dijkstra处理不了权值为负数的边
为什么dijkstra处理不了负边?
而且,dijkstra无法判断负环。
所以,只要我们将边权全部替换为正的,dijkstra就可以正常运行。
加大数让dijkstra处理负边
将图中每一项都加一个非常大的数。可以将负边换掉
但是这会使答案错误。举一个例子:
如图,当我们加上1时,本来的最短路为 1->2->3->4,长度为3;加上数后明显从1->4是最短的,长度为5,原最短路长度为6。
Part 2 概述
首先我们添加一个新节点,使这个节点与图上的每一个定点都联通,权值均为0。
这时候,我们对新节点用bellman_ford求最短路(bellman_ford可以处理负边,堆优化的SPFA不能处理负边)。得到的结果非0即负,用一个数组h将结果存起来。
这个时候,我们使用下面这个公式,来更新每一条边的边权。(w即为weight,权重。等号左边为新权值,等号右边为原权值+h[起点]-h[终点])
补丁:3->4的路径为-2+(-3-(-5)) = 0,少打了一个负号
此时图上已经没有负边了,我们可以放心大胆地dijkstra了。
针对每一个定点进行dijkstra,但是得到的距离表并不是原权值,而是经过bellman_ford的权值,所以我们需要进行换算。
之前是 + h(u) - h(v),现在反过来就是 + h(v) - h(u)了。
Part 3 正确性证明
Part 4 题目&代码
dotcpp并没有针对这个算法的题目,于是这里给下算法对应的题目。(希望管理添加这道题目)
题目描述
给定一个包含 n 个结点和 m 条带权边的有向图,求对于每一个节点i的下面这个式子的结果。(dis(i, j)表示i到j的距离,节点编号从1开始一直到n)
注意:
- 边权可能为负,且图中可能存在重边和自环。
- 数据卡spfa。
- 这题卡long long了,printf别忘了用lld。
输入数据
第 1 行:2 个整数 n,m,表示给定有向图的结点数量和有向边数量。
接下来 m 行:每行 3 个整数 u,v,w,表示有一条权值为 w 的有向边从编号为 u 的结点连向编号为 v 的结点。
输出数据
如果图中存在负环输出-1。
如果不存在负环:
对于每一个节点i输出上面那个式子。
如果不存在从i到j的路径,则路径长为10 ^ 9(1000000000),i到i距离为0.
输入输出样例
样例1
输入
5 7
1 2 4
1 4 10
2 3 7
4 5 3
4 2 -2
3 4 -3
5 3 4
输出
128
1000000072
999999978
1000000026
1000000014
样例2
输入
5 5
1 2 4
3 4 9
3 4 -3
4 5 3
5 3 -2
输出
-1
数据范围
n <= 3000,m <= 6000
代码
(由于数据过大,所以dis表必须开成全局变量,在函数内开不出来。)
//
// Created by Cat-shao on 2021/2/3.
//
#include <cstdio>
#include <deque>
#include <queue>
#include <cstring>
#include <climits>
using namespace std;
const long long INF = 1000000000;
const int MAX_N = 3100;
class edge {
public:
int to;
long long cost;
edge() {
}
edge(int _to, long long _cost) {
to = _to;
cost = _cost;
}
bool operator < (const edge &e) const {
return cost > e.cost;
}
};
class dequeGraph {
public:
deque<edge> vertex[MAX_N];
int v;
dequeGraph(int _v) {
v = _v;
}
void insert(int from, int to, long long cost) {
vertex[from].push_back(edge(to, cost));
}
void copy(dequeGraph *graph) {
for (int i = 1; i <= v; ++i) {
graph->vertex[i] = vertex[i];
}
}
};
void dij(dequeGraph *graph, long long dis[], int path[], int n, int s) {
bool mark[MAX_N];
priority_queue<edge> q;
memset(dis,