1 条题解
-
0
题目回顾
我们有一个 个节点、 条边的无向连通图。每条边有两种通行方式:
- 坐公交车:耗时 分钟
- 步行:耗时 分钟,且
要求在 时间段内不能乘坐公交车(只能步行或停留),必须在 时刻之前到达节点 。问:从节点 出发的最晚时间是多少?
核心思路
我们反过来思考:从终点 出发,倒着推导每个节点最晚可以什么时候出发,才能准时到达 。
设 表示从节点 出发,能够到达节点 且不晚于 的最晚出发时间。
显然:
- (在终点,最晚出发时间就是 本身)
- 对于其他节点,我们通过逆向的“松弛”操作来更新。
逆向松弛规则
考虑从节点 反向走到节点 (原图中 有一条边)。
已知从 出发的最晚时间是 ,我们要计算从 出发的最晚时间 。情况分析
如果从 在时间 出发,经过这条边到达 的时间是:
- 坐公交:
- 步行:
到达 的时间必须 (因为 是 的最晚出发时间,且出发后不能再等,否则会晚于 到达 的出发时刻)。
所以:
- 若坐公交:
- 若步行:
我们想要 最大 的 ,即 坐公交可行的最晚时间, 步行可行的最晚时间。
公交可行条件
坐公交时,必须避免在 期间乘车。
乘车时间段是 (因为 出发, 分钟后到达)。这个区间不能与 有重叠(不能部分重叠,也不能完全包含)。
等价条件:
要么 (在通话开始前结束)
要么 (在通话结束后开始)
计算
从 且公交可行,我们得到:
情况 A:若 ,则取 (因为此时 ,自动满足)。
情况 B:若 ,则取 (因为此时 ,自动满足)。
情况 C:若 ,则不能直接取 ,因为乘车会覆盖通话区间。
此时最晚可行是:在 时刻开始乘车,即 ,但这会导致 (因为 意味着 ),不满足到达时间 。
所以这种情况不能坐公交,只能考虑步行。
总结公式
设 。
如果 或 ,则公交可行,。
否则公交不可行,。
步行总是可行(因为通话期间可以步行),但步行耗时更长:
。最终:
特殊情况
如果 落在通话区间内,没问题,因为步行允许。
但注意:如果公交不可行,我们可能需要在起点等一会儿再出发,使得乘车区间避开通话。
等多久?等到 再出发,但这样到达 的时间是 ,必须 。
所以如果 ,那么 也是可行的。这在代码中体现为:
if(d1 == d - l2) d1 = max(d1, t1 - l1);实际上这里更准确的理解是:如果选择了步行方案,我们还可以考虑在起点等到 再坐公交?等一下,原代码这里有些简略,我们分析官方逻辑。
官方代码逻辑解读
int d1 = (d - l1 >= t2 || d <= t1) ? d - l1 : d - l2; if(d1 == d - l2) d1 = max(d1, t1 - l1);第一行:
- 如果 或 ,则公交可行,取 。
- 否则公交不可行,取步行 。
第二行:如果选了步行(即 ),再尝试一个优化:
在 时刻出发坐公交(到达 的时间是 ,必须 ,即 ),那么 也可以。
但注意:这里代码写的是 ?显然不对,应该是 。
实际上原代码有误?我们仔细看:
这个 很奇怪,可能是想表达:等到 再出发,但 是绝对时间, 也是绝对出发时间,不应该减 。更合理的解释:这里 可能是笔误,应该是 。但为了尊重原代码,我们保留其形式。实际上正确逻辑是:
- 如果步行,也可以选择等到 再出发坐公交(如果 ),那么 。
- 同时也可以等到 再出发坐公交(如果 ),取最大值。
所以官方代码简化了,只考虑了 的情况。
算法流程
- 初始化 ,其余 。
- 使用最大堆(或 set 按值降序)进行类 Dijkstra 的逆向松弛。
- 对于当前节点 的最晚时间 ,遍历所有邻边 :
- 计算 如上。
- 若 ,更新 并加入堆。
- 最终 即为从节点 出发的最晚时间。若为负数,输出 。
时间复杂度
每个节点和每条边最多被松弛一次,使用优先队列(或 set)复杂度 。
总 和不超过 ,可接受。
示例验证
以第一个样例为例:
- 边: 等
逆向从节点 开始:
- 边 :,,在 内,公交不可行,取步行 ,再尝试 ,取 。所以 。输出 ,符合样例。
总结
本题的关键是逆向思维 + 分段判断公交可行性,在 Dijkstra 框架下处理带时间窗口限制的最晚出发时间问题。
- 1
信息
- ID
- 6305
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者