You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.9 KiB
3.9 KiB
图论-多源最短路径(Floyd
算法)
一、Floyd
Floyd
算法是一次性求所有结点之间的最短距离,能处理负权边的图,程序比暴力的DFS
更简单,但是复杂度是O(n^3)
,只适合 n < 200
的情况。
Floyd
运用了 动态规划 的思想,求 i 、 j
两点的最短距离,可分两种情况考虑,即经过图中某个点 k
的路径和不经过点 k
的路径,取两者中的最短路径。
- 判断负圈
眼尖的人儿可能发现邻接矩阵
mp
中,mp[i][i]
并没有赋初值0
,而是inf
。并且计算后mp[i][i]
的值也不是0
,而是mp[i][i]=mp[i][u]+……+mp[v][i]
,即从外面绕一圈回来的最短路径,而这正 用于判断负圈,即mp[i][i]<0
。
相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包
记录坑点:重复边,保留最小的那个。
二、模板
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (g[i][k] != inf) //优化
for (int j = 1; j <= n; j++)
if (g[i][j] > g[i][k] + g[k][j])
g[i][j] = g[i][k] + g[k][j];
}
三、例题
POJ-3259
Wormholes
类型 判负环
题意
- 正常路是
m
条双向正权边 - 虫洞是
w
条单向负权边 - 题目让判断是否有负权回路
办法
利用Floyd
找两点间花费的最短时间,判断从起始位置到起始位置的最短时间是否为负值(判断负权环),若为负值,说明他通过虫洞回到起始位置时比自己最初离开起始位置的时间早。
代码实现:
在第二重循环,求完第i
个结点后判断。i
到i
之间的最短距离是一个负值,说明存在一个经过它的负环。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 502;
int n, m, w;
int g[N][N];
// floyd判断是否存在负圈
bool floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (g[i][k] != INF) { // 优化
for (int j = 1; j <= n; j++)
if (g[i][j] > g[i][k] + g[k][j])
g[i][j] = g[i][k] + g[k][j];
if (g[i][i] < 0) return true; // 发现负圈
}
return false;
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> m >> w;
memset(g, INF, sizeof g); // 初始化邻接矩阵
// 双向正值边
while (m--) {
int a, b, c;
cin >> a >> b >> c;
// 注意坑:重边
g[a][b] = g[b][a] = min(c, g[a][b]);
}
// 单向负值边
while (w--) {
int a, b, c;
cin >> a >> b >> c;
g[a][b] = -c; // 负值边
}
if (floyd())
puts("YES");
else
puts("NO");
}
return 0;
}
HDU-1385
Minimum
Transport
Cost
类型 打印路径
题意 给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,如果有多条路径则输出字典序最小的那条路径。注意,起点城市和终点城市不需要收城市税。
分析
输出路径,多个答案则输出字典序最小的,无法到达输出-1
。
读入邻接表, cost[]
记录每个城市额外费用, path[][]
记录路径,比如 path[i][j]=k
表示 i
到 j
的路径是 i
先到 k
,再从 k
到 j
,floyd()
里维护即可。然后处理下输出(比较恶心)。