GESP202512 八级 · 真题
难度 普及+/提高

P14923 猫和老鼠

最短路 + “反向限额”双 Dijkstra:判断每个点能不能全程安全逃回鼠洞。

一、先回答问题

一句话

猫走最短路;点 u 安全,当且仅当老鼠能选一条 u→鼠洞 的路径,路径上每个点 x 老鼠的到达时间都 严格小于 猫的最短到达时间 D[x]。求所有安全点的奶酪和。

两步走:①从猫窝跑一次 DijkstraD[x](猫到各点最短时间);②从鼠洞做一次 “取最大”的反向 Dijkstra,求每点的最晚出发限额 G[x]G[i]>0 的点就是安全点。

课堂安排:先讲清“安全=路径上处处早于猫”,再推为什么限额能反向传播,最后手算样例 1 得 22。

二、核心观察

为什么能反向推

定义 G[x] = “在点 x 最晚什么时刻出发,还来得及安全逃到鼠洞”。在鼠洞 b,安全的硬上限就是猫到达它的时间:G[b]=D[b]

若从 v 走一条边 (v,u,w)u,则在 v 的限额受两条约束:到 v 本身要早于猫 D[v];走到 u 后还要满足 u 的限额,即出发时刻 +w < G[u]。合并:

G[v] = max( G[v], min( D[v], G[u] - w ) ) —— 取“能用的最大限额”,所以用大根堆反向跑 Dijkstra。

最后 G[i]>0 表示“时刻 0 待在 i 仍安全”,即 i 是安全点。

三、互动演示(样例 1)

图:5 点,猫窝 a=1,鼠洞 b=2,边权见连线。奶酪 c=[1,2,4,8,16]。

c=11
c=22
c=43
c=84
c=165

边:1-2(4) · 2-3(3) · 3-4(1) · 2-5(2) · 3-1(8)

四、自己选答案

Q1.D[x](猫到各点最短时间)该用哪种算法?

Q2. 反向传播限额时,优先队列应当每次取出?

Q3. 为什么 DG 和奶酪和都要用 long long

五、手推结果

点 x12(鼠洞)345
D[x] 猫最短04786
G[x] 限额04102
安全? (G>0)
奶酪124816
安全点 = {2,3,5},奶酪和 = 2 + 4 + 16 = 22

六、C++14 参考代码

读代码三处重点:①两个 Dijkstra 共用同一张邻接表;②第二个用 priority_queue<Node> 默认大根堆取最大限额;③统计时只累加 G[i]>0 的点(严格大于,等于不算)。
常见错误:忘记 long long(距离/总和溢出);把“严格大于”写成“大于等于”;初始化 G 要用 -INF 而不是 0。

复杂度:两次类 Dijkstra,O(m log n);空间 O(n+m)。迁移:P1396 营救(路径最大边)、P1073 最优贸易(图上 DP 式更新)。