什么?Floyd?sb O(n ^ 3) 算法早不用了,右上角红叉吧。
我之前虽然也认识过 Floyd 算法的重要性,不过多少也是这么想的。
然而最近三天连续 rand 到了好几道有关的题目,让我彻底重新审视了 Floyd —— 既然能够作为一个重要的算法流传至今,那自有他的重要之处。

Floyd 是一个求解所有点对间的最短路算法,也可能是绝大多数人接触的最早的最短路算法。它适用于无负权边的图,时间复杂度约为 O(n ^ 3) 。因为时间复杂度太高了,所以也是很多人起初都对它有些成见的原因,再加上任意点对间最短路用的又少,会误认为这个算法后期就一无是处了。
众所周知,Floyd 的本质是动态规划。对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是就更新它。正是由于这个动态规划思想的精髓所在,以及一层层更新的特性,使得 Floyd 大有用武之地。
废话不多说了,反正四行的 Floyd 没人不会……

万恶之源那天,我正在 Luogu 愉快地随机跳题,于是就 rand 到了 P2103 道路值守
很开心,这不就是个最短路计数?正巧前一天就 A 了一道类似的题,很快就敲出来准备 AC 了。结果嘛……
怎么办,蒟蒻 Nanjo_Qi 瞬间就没有思路了,于是只能求助题解。
结果是个用到 Floyd 的题目。

Floyd 的重要特性是:全面枚举,有序更新。

Floyd题:https://www.luogu.org/problemnew/show/P2103
这个也是:https://www.luogu.org/problemnew/show/P1476
这个也是:https://www.luogu.org/problemnew/show/P1119

 

P2103 道路值守:

利用 Floyd 层层松弛更新,以及循环遍历所有点对的特性,完全无需考虑时间复杂度;
首先求出所有点对间最短路,然后枚举寻找那些可以作为方案数加入的点(另一条与最短路长度相等,或中途汇入最短路的道路,以此形成可行方案),分别在每个汇入点记录,最后累计。

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 500 + 10;
 8 int n, m, g[maxn][maxn], dis[maxn][maxn], method[maxn][maxn];
 9 
10 int main(int argc, char const *argv[])
11 {
12   memset(dis, 0x3f, sizeof dis);
13   scanf("%d%d", &n, &m);
14   for(int i = 1; i <= m; ++i) {
15     int u = 0, v = 0, w = 0;
16     scanf("%d%d%d", &u, &v, &w);
17     dis[u][v] = dis[v][u] = g[u][v] = g[v][u] = w;
18   }
19   for(int i = 1; i <= n; ++i) dis[i][i] = 0;
20   for(int k = 1; k <= n; ++k)
21     for(int i = 1; i <= n; ++i)
22       for(int j = 1; j <= n; ++j)
23         dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
24 
25   for(int i = 1; i <= n; ++i) {
26     int tmp[maxn] = {0};
27     for(int j = 1; j <= n; ++j) if( i != j && dis[i][j] != dis[0][0] )
28       for(int k = 1; k <= n; ++k) if( g[k][j] )
29         if( dis[i][j] == dis[i][k] + g[k][j] ) ++tmp[j];
30 
31     for(int j = 1; j <= n; ++j) if( i != j )
32       for(int k = 1; k <= n; ++k)
33         if( dis[i][j] == dis[i][k] + dis[k][j] ) method[i][j] += tmp[k];
34   }
35 
36   for(int i = 1; i <= n; ++i)
37     for(int j = i + 1; j <= n; ++j)
38       printf("%d ", method[i][j]);
39 
40   // printf("_______________________________________________\n");
41   // printf("Process Exited Correctly With A Return Value 0.\n");
42   // printf("All Rights Reserved By Kimitsu Nanjo In 2018.\n\n");
43   return 0;
44 }
View Code

相关文章: