T3620.恢复网络路径
graph, topological sort, binary search, https://leetcode.cn/problems/network-recovery-pathways/
给你一个包含 n 个节点(编号从 0 到 n - 1)的有向无环图。图由长度为 m 的二维数组 edges 表示,其中 edges[i] = [ui, vi, costi] 表示从节点 ui 到节点 vi 的单向通信,恢复成本为 costi。
一些节点可能处于离线状态。给定一个布尔数组 online,其中 online[i] = true 表示节点 i 在线。节点 0 和 n - 1 始终在线。
从 0 到 n - 1 的路径如果满足以下条件,那么它是 有效 的:
- 路径上的所有中间节点都在线。
- 路径上所有边的总恢复成本不超过
k。
对于每条有效路径,其 分数 定义为该路径上的最小边成本。
返回所有有效路径中的 最大 路径分数(即最大 最小 边成本)。如果没有有效路径,则返回 -1。
示例 1:
输入: edges = [[0,1,5],[1,3,10],[0,2,3],[2,3,4]], online = [true,true,true,true], k = 10
输出: 3
解释:

- 图中有两条从节点 0 到节点 3 的可能路线:
- 路径
0 → 1 → 3- 总成本 =
5 + 10 = 15,超过了 k (15 > 10),因此此路径无效。
- 总成本 =
- 路径
0 → 2 → 3- 总成本 =
3 + 4 = 7 <= k,因此此路径有效。 - 此路径上的最小边成本为
min(3, 4) = 3。
- 总成本 =
- 路径
- 没有其他有效路径。因此,所有有效路径分数中的最大值为 3。
示例 2:
输入: edges = [[0,1,7],[1,4,5],[0,2,6],[2,3,6],[3,4,2],[2,4,6]], online = [true,true,true,false,true], k = 12
输出: 6
解释:

- 节点 3 离线,因此任何通过 3 的路径都是无效的。
- 考虑从 0 到 4 的其余路线:
- 路径
0 → 1 → 4- 总成本 =
7 + 5 = 12 <= k,因此此路径有效。 - 此路径上的最小边成本为
min(7, 5) = 5。
- 总成本 =
- 路径
0 → 2 → 3 → 4- 节点 3 离线,因此无论成本多少,此路径无效。
- 路径
0 → 2 → 4- 总成本 =
6 + 6 = 12 <= k,因此此路径有效。 - 此路径上的最小边成本为
min(6, 6) = 6。
- 总成本 =
- 路径
- 在两条有效路径中,它们的分数分别为 5 和 6。因此,答案是 6。
提示:
n == online.length2 <= n <= 5 * 10^40 <= m == edges.length <= min(10^5, n * (n - 1) / 2)edges[i] = [ui, vi, costi]0 <= ui, vi < nui != vi0 <= costi <= 10^90 <= k <= 5 * 10^13online[i]是true或false,且online[0]和online[n - 1]均为true。- 给定的图是一个有向无环图。
下面是解决该问题的思路和一种高效的算法实现:
解题思路
基本约束过滤:
- 路径中的所有中间节点都必须在线。因此,如果一个节点的
online[i] = false(且该节点不是起点0或终点n-1),那么它就不能作为路径的一部分。 - 我们可以直接过滤掉所有连接到离线节点的边,即只保留两个端点均在线的边。
- 路径中的所有中间节点都必须在线。因此,如果一个节点的
可达性过滤(双向 BFS):
- 有些节点虽然在线,但无法从起点
0到达,或者无法到达终点n-1。为了提高二分查找时的性能,我们可以通过正向 BFS(从0开始)和反向 BFS(从n-1开始)计算出所有既可由起点到达,又能到达终点的有效节点集合valid_set。 - 如果起点
0无法到达终点n-1,则直接返回-1。
- 有些节点虽然在线,但无法从起点
拓扑排序:
- 在有向无环图 (DAG) 中,求单源最短路径最有效率的方法是在拓扑序上进行动态规划 (DP)。
- 重新建图,仅保留两个端点都在
valid_set里的有效边。接着在valid_set的子图上跑拓扑排序,得到拓扑序列topo。
二分查找:
- 路径分数定义为路径上所有边的最小成本。我们可以二分这个“最小成本”的可能取值
。 的备选值只能是图中出现过的边成本。我们收集所有有效边的成本,去重并排序,作为二分查找的候选区间。 - 对于每次二分猜测的
值: - 我们仅使用成本
的边进行 DP,求从 0到n-1的最短路径。 - 如果最短路径总成本
,则该 是可行的,尝试更大的值;否则,尝试更小的值。
- 我们仅使用成本
- 路径分数定义为路径上所有边的最小成本。我们可以二分这个“最小成本”的可能取值
Python3 代码实现
python
class Solution:
def findMaxPathScore(self, edges: list[list[int]], online: list[bool], k: int) -> int:
n = len(online)
# 1. 过滤:只保留两个端点均为在线的边
valid_edges = []
for u, v, cost in edges:
if online[u] and online[v]:
valid_edges.append((u, v, cost))
# 2. 双向 BFS 确定既可从 0 到达,又可到达 n-1 的有效节点集合
adj_f = [[] for _ in range(n)]
adj_b = [[] for _ in range(n)]
for u, v, cost in valid_edges:
adj_f[u].append(v)
adj_b[v].append(u)
visited_f = [False] * n
visited_f[0] = True
q = [0]
head = 0
while head < len(q):
u = q[head]
head += 1
for v in adj_f[u]:
if not visited_f[v]:
visited_f[v] = True
q.append(v)
# 若 0 无法到达 n - 1,直接返回 -1
if not visited_f[n - 1]:
return -1
visited_b = [False] * n
visited_b[n - 1] = True
q = [n - 1]
head = 0
while head < len(q):
u = q[head]
head += 1
for v in adj_b[u]:
if not visited_b[v]:
visited_b[v] = True
q.append(v)
# 取得双向可达节点的交集
valid_set = {i for i in range(n) if visited_f[i] and visited_b[i]}
# 3. 重新建图,仅包含有效节点之间的边,并统计入度以进行拓扑排序
adj = [[] for _ in range(n)]
in_degree = [0] * n
costs = set()
for u, v, cost in valid_edges:
if u in valid_set and v in valid_set:
adj[u].append((v, cost))
in_degree[v] += 1
costs.add(cost)
# 4. 拓扑排序 (在有效子图中,0 为唯一的源点)
queue = [0]
topo = []
head = 0
while head < len(queue):
u = queue[head]
head += 1
topo.append(u)
for v, _ in adj[u]:
in_degree[v] -= 1
if in_degree[v] == 0:
queue.append(v)
# 排序所有可能达到的边权值
sorted_costs = sorted(list(costs))
# 5. 二分查找
def check(mid_val, topo=topo, adj=adj, k=k, n=n):
dp = [10**18] * n
dp[0] = 0
for u in topo:
dist = dp[u]
if dist > k: # 剪枝:若当前距离已大于 k,则其后续点总花费必然大于 k
continue
for v, cost in adj[u]:
if cost >= mid_val:
new_dist = dist + cost
if new_dist < dp[v]:
dp[v] = new_dist
return dp[n - 1] <= k
ans = -1
left = 0
right = len(sorted_costs) - 1
while left <= right:
mid = (left + right) // 2
if check(sorted_costs[mid]):
ans = sorted_costs[mid]
left = mid + 1
else:
right = mid - 1
return ans复杂度分析
时间复杂度:
- 双向 BFS 及拓扑排序耗时
。 - 二分查找的范围为有效边成本的个数,最多进行
次判断。 - 每次判定(
check函数)中,利用拓扑序进行 DP 的时间复杂度为,其中 , 是有效子图的节点和边数。 - 综上,总时间复杂度为
。在 且 的情况下,能够在可接受的时间内平稳运行。
- 双向 BFS 及拓扑排序耗时
空间复杂度:
,主要来自于构建的正反向邻接表和 DP 数组。