1 条题解
-
0
这道题的核心在于利用最短路的性质,通过巧妙的更新和堆维护,避免对每次断边都重新跑最短路。
下面用一个表格来梳理解决该题的关键思路:
核心思路 关键操作 技巧与说明 性质挖掘:删除最短路上的边后,新的最短路通常是 从原路径某点离开 → 绕行 → 再回到原路径 。 将原最短路上的点按顺序编号。 可将问题转化为对绕行路径的评估。 逆向贡献:按最短路顺序枚举断边,用堆维护当前所有可行的绕行方案。 枚举断边时,从该边起点出发SPFA松弛,将能回到原路径的绕行方案(终点编号>当前断边编号)及其总距离加入堆。 堆顶即当前可能的最优解。 动态维护:随着枚举断边编号增大,一些绕行方案的"返回点"已过断边,变得无效。 每次从堆中弹出所有"返回点"不大于当前断边编号的方案(因其未成功绕过断边)。 保证堆中方案均有效绕过当前断边。 🛠️ 实现步骤详解
以下是基于上述思路的实现步骤,主要参考:
-
预处理:
- 读取输入:存储图结构,并记录交通部指定的最短路径
rode[]及其边。 - 编号与标记:对最短路径依次经过的点进行顺序编号(
num[]),并标记最短路径上的边(viss[])。 - 计算后缀和:从终点
N开始,逆着最短路径 计算每个编号的点到终点N的距离disn[]。例如disn[i] = disn[i+1] + edge[rode[i]].w。
- 读取输入:存储图结构,并记录交通部指定的最短路径
-
初始化:
- 初始化一个最小堆,用于存储绕行方案。每个方案是一个
(返回点的编号, 该方案的总距离)的二元组,按总距离排序。 - 初始化到起点的最短路数组
dis1[],例如dis1[1] = 0。
- 初始化一个最小堆,用于存储绕行方案。每个方案是一个
-
处理每条断边: 按最短路径顺序,依次枚举要删除的边
rode[i]:- 清理堆:弹出堆顶所有返回点编号 ≤ 当前断边编号
i的方案,因为它们没有成功绕过当前断边。 - 输出答案:如果堆为空,输出
-1;否则,堆顶元素的距离就是当前答案。 - 更新最短路:将当前断边
rode[i]的权值累加到其起点的dis1值上,得到其终点的dis1值。此操作模拟了在原始最短路上前进。 - SPFA探索绕行:从当前断边的终点
edge[rode[i]].v开始进行 SPFA:- 在松弛过程中,禁止使用最短路径上的边(通过
viss[]标记判断)。 - 如果松弛到一个不在最短路径上的点,则正常加入队列。
- 如果松弛到一个在最短路径上的点(即
num[v] > 0),则意味着找到了一条绕行路径,它从断边后某点离开,最终又回到了最短路径上的点v。此时,将(num[v], dis1[v] + disn[num[v]])加入堆。这里dis1[v]是绕行到达v的距离,disn[num[v]]是v沿最短路径到终点的距离,相加即为此绕行方案总长。
- 在松弛过程中,禁止使用最短路径上的边(通过
- 清理堆:弹出堆顶所有返回点编号 ≤ 当前断边编号
💡 复杂度与提示
- 时间复杂度:约为 。由于SPFA的效率和图的结构有关,且堆操作本身较快,此方法在随机数据下表现尚可,但可能被特殊数据卡超时。
- 实现注意:
- SPFA时,只对
dis1数组进行初始化,disn数组是预处理好的固定值。 - 堆中存储的是返回点在最短路径上的编号,而非节点编号,便于判断方案是否有效。
- 谨慎处理边的编号与图中节点关系的映射。
- SPFA时,只对
💎 总结
这道题的关键在于转换视角:不从"删除"入手,而是关注新增的绕行路径,并利用堆动态维护有效解。
-
- 1
信息
- ID
- 5682
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者