- Floyd 求最短路,时间复杂度 O ( n 3 ) O(n^3) O(n3),空间复杂度 O ( n 2 ) O(n^2) O(n2)。
for (int k = 1;k <= n;++k)
for (int i = 1;i <= n;++i)
for (int j = 1;j <= n;++j) dis[i][j] = min (dis[i][j],dis[i][k] + dis[k][j]);
- 【Q1】该算法的正确性?
算法基于动态规划。设 f k , i , j f_{k,i,j} fk,i,j 表示只能借助于 1 , 2 , ⋯ , k 1,2,\cdots,k 1,2,⋯,k 点时, i → j i \to j i→j 的最短路径。那么根据是否经过 k k k 点,可以列出状态转移方程:
f k , i , j = min ( f k − 1 , i , j , f k − 1 , i , k + f k − 1 , k , j ) f_{k,i,j} = \min (f_{k - 1,i,j},f_{k - 1,i,k} + f_{k - 1,k,j}) fk,i,j=min(fk−1,i,j,fk−1,i,k+fk−1,k,j)
很好理解,后者相当于以 k k k 作为中转站,即 i → k → j i \to k \to j i→k→j 的转移。那么当处理询问 u , v u,v u,v 时, f n , u , v f_{n,u,v} fn,u,v 即为所求。
可以发现 f k , i , j f_{k,i,j} fk,i,j 只对 f k − 1 , i ′ , j ′ f_{k - 1,i',j'} fk−1,i′,j′ 产生依赖,不难想到可以滚动数组优化。那么,Floyd 最终直接删去那一维,这样做不会出错吗?
现在我们去掉第一维,只要保证在转移时方程右侧的 f i , j , f i , k , f k , j f_{i,j},f_{i,k},f_{k,j} fi,j,fi,k,fk,j 都是未被覆盖过的即可。当最外层为第 k k k 次循环时,显然 f i , j f_{i,j} fi,j 还未被覆盖。对于 f i , k , f k , j f_{i,k},f_{k,j} fi,k,fk,j,按照之前的三维转移方程,可以得到 f k , i , k = min ( f k − 1 , i , k , f k − 1 , i , k + f k − 1 , k , k ) = f k − 1 , i , k f_{k,i,k} = \min (f_{k - 1,i,k},f_{k - 1,i,k} + f_{k - 1,k,k}) = f_{k - 1,i,k} fk,i,k=min(fk−1,i,k,fk−1,i,k+fk−1,k,k)=fk−1,i,k, f k , j f_{k,j} fk,j 同理。因此我们证明了空间优化的该算法的正确性。
与此同时,从动态规划的角度出发,“为什么 k k k 循环需要放在最外层?”也就很好理解了。
【Q2】若每次修改一条边的权值,有必要重新完整的去做一遍 Floyd 算法吗?
答案是否定的,只需要 O ( n 2 ) O(n^2) O(n2) 即可实现局部更新。设 ( u , v ) (u,v) (u,v) 的权值更新为 w ( u , v ) w(u,v) w(u,v)(无向图),则可得到转移方程:
f i , j = min ( f i , j , min ( f i , u + w ( u , v ) + f v , j , f i , v + w ( v , u ) + f u , j ) ) f_{i,j} = \min (f_{i,j},\min (f_{i,u} + w(u,v) + f_{v,j},f_{i,v} + w(v,u) + f_{u,j})) fi,j=min(fi,j,min(fi,u+w(u,v)+fv,j,fi,v+w(v,u)+fu,j))
例题:
Another Exercise on Graphs (easy version)
Another Exercise on Graphs (hard version)
解析:
根据边权排序,然后答案显然就是某一条边的值。当枚举到第 i i i 条边的时候,比其小的边均置为 0 0 0,否则为 1 1 1。若 i → j i \to j i→j 的最短路是 w i w_i wi,那么第 i + 1 i + 1 i+1 大的便是这条边。
进一步的, E2 \texttt{E2} E2 的 m m m 很大,可是我们会发现有效的更新只有 n − 1 n - 1 n−1 次,因为之后 ( i , j ) (i,j) (i,j) 的最短路已经为 0 0 0,不会再发生变化,并查集维护即可。
【Q3】变式 1 1 1 【模板】传递闭包
简单来说,如果原关系图上有 i i i 到 j j j 的路径,则其传递闭包的关系图上就应有从 i i i 到 j j j 的边,均用 0 / 1 0/1 0/1 表示。因此枚举中转点 k k k,若 ( i , k ) (i,k) (i,k) 与 ( k , j ) (k,j) (k,j) 均有连边,则可将 ( i , j ) (i,j) (i,j) 连边。写成表达式,就是:
f i , j = max ( f i , j , min ( f i , k , f k , j ) ) f_{i,j} = \max (f_{i,j},\min (f_{i,k},f_{k,j})) fi,j=max(fi,j,min(fi,k,fk,j))
【Q4】变式 2 2 2 [USACO07NOV] Cow Relays G 本质同 Walk
设 A A A 中是经过 x x x 条边的最短路, B B B 中是经过 y y y 条边的最短路。通过以下转移后,可以发现 C C C 中是经过 x + y x + y x+y 条边的最短路:
C i , j = min ( C i , j , A i , k + B k , j ) C_{i,j} = \min ({C_{i,j}},A_{i,k} + B_{k,j}) Ci,j=min(Ci,j,Ai,k+Bk,j)
这样我们的转移就变得简单,重载后直接通过矩阵快速幂即可解决问题。
进一步的变式 [SCOI2009] 迷路,衡量距离。拆点与 bitset 的优化,时间复杂度
O
(
n
3
64
log
k
)
O(\frac{n^3}{64} \log k)
O(64n3logk)。具体来说,对于一个点,拆成
v
1
∼
v
10
v_1 \sim v_{10}
v1∼v10,然后
u
→
v
u \to v
u→v 一条边为
w
w
w 的边,然后
v
i
→
v
i
−
1
v_i \to v_{i - 1}
vi→vi−1 进行连边,于是可以变为
u
→
v
w
u \to v_w
u→vw。
3万+

被折叠的 条评论
为什么被折叠?



