CSP-J 图论 · Floyd 第 2 梯 · 理解 k 的本质
P1119 灾后重建
同样是问最短路,但点会随时间一个个“修好”。这正好对上 Floyd 最外层 k 的含义。
一、先回答问题
结论
每修好一个村庄,就把它当作一个新的中转点 k,跑一层 Floyd。
因为询问时间不下降,所有询问只需一遍 Floyd 就能边解锁边回答。
先别这么写
每个询问都把可用点重新跑一遍完整 Floyd?
那是 O(询问 × n³),会超时。关键是不要重复劳动。
二、核心观察
关键对应
Floyd 最外层 k 的含义是:“现在允许把第 k 个点当中转站”。
而本题里村庄是按修复时间先后一个个变得可用的 —— 这不就是
“按顺序开放中转点”吗?于是把村庄按修复时间排序,修好一个就跑一层对应的 k,天衣无缝。
三、互动演示:时间推进,村庄逐个解锁
样例 4 个村庄(编号 0~3),修复时间分别是 1、2、3、4 天。问 0→1 的最短路。
四、自己选答案
Q1. 为什么本题不能每次询问都重跑完整 Floyd?
Q2. 处理询问前,村庄应该按什么排序?
Q3. 询问 (x, y, t),什么时候必须输出 -1?
五、手推结果(样例 4 个询问)
| 询问 | 此刻已修好 | 0→1 | 输出 |
| t=2,问 0→2 | 0,1 | 村庄2没修好 | -1 |
| t=2,问 0→1 | 0,1 | ∞(不通) | -1 |
| t=3,问 0→1 | 0,1,2 | 0→2→1 = 5 | 5 |
| t=4,问 0→1 | 0,1,2,3 | 0→2→3→1 = 4 | 4 |
六、C++14 参考代码(核心片段)
注意那个不断前进的指针 k
① 村庄按修复时间排序,用指针 now 记录“已经解锁到第几个”。
② 每解锁一个村庄,就以它为 k 跑一层(两重 i,j 循环),而不是整套三重循环。
③ 回答前先判:x、y 都已解锁,且 dist[x][y]≠INF,否则输出 -1。
七、接着练(同梯度)
- 基础:B3647 模板 Floyd —— 先把朴素三重循环写熟。
- 巩固:P3905 道路重建 —— 另一种“边权设计”思路。
- 提升:P1841 重要的城市 —— Floyd 后对最短路矩阵做分析(提高+)。