typedef std::vector<std::vector<longlong> > Graph; // Graph[u][v] is the weight, 0 for no conn
0x01. Floyd-Warshall 算法浅析
Floyd-Warshall 算法(简称 Floyd 算法)由 Robert Floyd 于 1962 年发表(但据悉其本质与 Bernard 与 1959 年以及 Stephen Warshall 于 1962 年发表的用于寻找图的传递闭包的算法相同, 反正历史这一块笔者也不是很懂也懒得更深入查证了 ),是一种 一次性计算出图上任意两点间最短路径 的算法,且可以处理存在负边权的情况(不能处理负环)
Floyd-Warshall 算法的基本原理是 动态规划 ,我们令 dist[i][j] 为已知的从 i 到 j 的最短距离(在不考虑经过顶点 k 的情况下),当我们将 k 纳入考虑范畴时,从 i 到 j 之间便多出来一条新的路:先从 i 出发到 k,再从 k 出发到 j,此时最短路为 从 i 到 j 的原最短路 与 从 i 到 k 的最短路 + 从 k 到 j 的最短路 之间的最小值,即进行如下更新操作:
for (int k = 0; k < n; k++) { // try to add a new k into every i-j pair for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); } } }
Floyd-Warshall 算法的核心思路不难理解: dp[i][j] 记录当前从 i 到 j 的最短距离,我们就对于每个 k,都重复遍历一遍所有的点距离,尝试将 k 纳入到 i -> j 的路径中,即如果 i -> k 加上 k -> j 这两段路的总和小于 i -> j 的原长度((dp[i][k] + dp[k][j]) < dp[i][j] ),则更新 i -> j 的距离为 i -> k -> j
那么问题来了,我们很容易产生这样的疑惑:
为什么 k 要放在最外层循环?
为什么这么算最终能得到正确的最短路结果?
我们先来解决第一个问题,k 放在最外层循环,本质上是 允许每一次多添加一个点 k 作为中转 ,即第一次外层大循环是允许将点 1 纳入到中转,遍历整个图的所有路径查看是否存在一条路径将 1 作为中转会使得整个路径变短,之后的循环便是逐渐将点 2、点 3、…、点 k 给纳入可达的中转范围,即每层外层循环增加一个允许的中转点(第一层循环允许点集合 {1} 作为中转,第二层循环允许点集合 {1,2} 作为中转……)
intmain(int argc, char **argv, char **envp) { int n;
cin >> n;
int dp[n][n];
for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> dp[i][j]; } }
for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (dp[i][k] != 0 && dp[k][j] != 0) { if (dp[i][j] == 0) { dp[i][j] = dp[i][k] + dp[k][j]; } else { dp[i][j] = min(dp[i][k] + dp[k][j], dp[i][j]); } } } } }
for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (dp[i][j] != 0) { cout << 1; } else { cout << 0; }
if (j != (n - 1)) { cout << ' '; } }
cout << endl; }
return0; }
轻松 AC:
0x02. Johnson 算法浅析
Johnson 算法是另外一种能计算全源最短路的算法,由 Donald B. Johnson 于 1977 年提出,其基本思想来源于对单源最短路算法 Dijkstra 的推广,众所周知一个不难想到的求最短路算法是可以直接对所有的点跑一轮最短路算法,而 Dijkstra 是求单源最短路的经典算法(并且已经在 FOCS 2024 上被证明为最优),其时间复杂度可比 Bellman-Ford 好太多了,所以我们不难想到的是可以直接对所有的点跑一轮最短路算法,就能直接得到这个图的全源最短路了——
for (auto u = 1; u <= node_nr; u++) { if (dist[u] == LLONG_MAX) { continue; }
for (auto &edge : graph[u]) { auto v = edge.first; auto w = edge.second; auto new_dist = dist[u] + w; if (dist[v] > new_dist) { dist[v] = new_dist; has_relax = true; } } }
if (!has_relax) { break; }
loop_nr++; }
if (loop_nr == (node_nr - 1)) { /* negative loop */ bool has_relax = false; for (auto u = 1; u <= node_nr; u++) { if (dist[u] == LLONG_MAX) { continue; }
for (auto &edge : graph[u]) { auto v = edge.first; auto w = edge.second; auto new_dist = dist[u] + w;
dist.resize(node_nr + 1); new_graph.resize(node_nr + 2); for (i64 v = 1; v <= node_nr; v++) { new_graph[new_node].push_back({v, 0}); }
if (!bellman_ford(new_graph, new_node, node_nr + 1, h_dist)) { returnfalse; }
for (auto u = 1; u <= node_nr; u++) { for (auto i = 0; i < graph[u].size(); i++) { auto v = graph[u][i].first; auto old_w = graph[u][i].second; auto new_w = old_w + (h_dist[u] - h_dist[v]); new_graph[u][i].second = new_w; } }
for (auto u = 1; u <= node_nr; u++) { dist[u] = dijkstra(new_graph, u, node_nr);
for (i64 v = 1; v <= node_nr; v++) { if (dist[u][v] != LLONG_MAX) { dist[u][v] = dist[u][v] - h_dist[u] + h_dist[v]; } } }
for (auto u = 1; u <= node_nr; u++) { if (dist[u] == LLONG_MAX) { continue; }
for (auto &edge : graph[u]) { auto v = edge.first; auto w = edge.second; auto new_dist = dist[u] + w; if (dist[v] > new_dist) { dist[v] = new_dist; has_relax = true; } } }
if (!has_relax) { break; }
loop_nr++; }
if (loop_nr == (node_nr - 1)) { /* negative loop */ bool has_relax = false; for (auto u = 1; u <= node_nr; u++) { if (dist[u] == LLONG_MAX) { continue; }
for (auto &edge : graph[u]) { auto v = edge.first; auto w = edge.second; auto new_dist = dist[u] + w;
dist.resize(node_nr + 1); new_graph.resize(node_nr + 2); for (i64 v = 1; v <= node_nr; v++) { new_graph[new_node].push_back({v, 0}); }
if (!bellman_ford(new_graph, new_node, node_nr + 1, h_dist)) { returnfalse; }
for (auto u = 1; u <= node_nr; u++) { for (auto i = 0; i < graph[u].size(); i++) { auto v = graph[u][i].first; auto old_w = graph[u][i].second; auto new_w = old_w + (h_dist[u] - h_dist[v]); new_graph[u][i].second = new_w; } }
for (auto u = 1; u <= node_nr; u++) { dist[u] = dijkstra(new_graph, u, node_nr);
for (i64 v = 1; v <= node_nr; v++) { if (dist[u][v] != LLONG_MAX) { dist[u][v] = dist[u][v] - h_dist[u] + h_dist[v]; } } }
returntrue; }
intmain(int argc, char **argv, char **envp) { i64 n, m;
cin >> n >> m;
i64 node_nr = n; Graph graph(node_nr + 1);
for (auto i = 0; i < m; i++) { i64 u, v, w; cin >> u >> v >> w; graph[u].push_back({v, w}); }