3620. Network Recovery Pathways
- Graph
- Binary Search
- Dynamic Programming
- Topological Sort
- Shortest Path
TIME
O((n + m) log m)
SPACE
O(n + m)
Intuition
The score of a path is its minimum edge cost, and we want the largest such minimum subject to a total-cost budget k. "Maximize the minimum" with a monotonic feasibility check screams binary search on the answer.
Fix a threshold x. A path can achieve score >= x only if every edge on it has cost >= x. So restrict to edges with cost >= x; a valid path with score >= x exists iff, within that subgraph, the cheapest total-cost path from 0 to n-1 is <= k. As x grows the subgraph shrinks, so feasibility is monotonically non-increasing, binary-searchable.
Approach
- Collect the distinct edge costs and sort them; these are the only candidate answers (the score always equals some edge's cost).
- Precompute a topological order of the DAG once (Kahn's algorithm).
- Feasibility
feasible(x): min-total-cost DP over the topo order.dist[0] = 0, everything elseINF.- Relax each edge
u -> vonly ifcost >= xand both endpoints are online (an offline node can never lie on a valid path):dist[v] = min(dist[v], dist[u] + cost). - Feasible iff
dist[n-1] <= k.
- Binary-search the largest candidate cost
xwithfeasible(x); if even the smallest threshold (all edges) is infeasible, return-1.
Complexity
- Time: O((n + m) log m),
log mfeasibility checks, each an O(n + m) topo DP - Space: O(n + m), adjacency/topo structures and the
distarray
Edge Cases
- No path within budget →
-1 - Offline intermediate nodes prune edges (endpoints 0 and n-1 always online)
kup to 5·10^13 and path sums up to ~n·10^9 → use 64-bit (long);distsentinel must exceed any real sum- Zero-cost edges → threshold
0is a valid candidate (a path of all-0 edges scores 0) m = 0→ no path unless trivial →-1
Code
from typing import List
from collections import deque
class Solution:
def findMaxPathScore(self, edges: List[List[int]], online: List[bool], k: int) -> int:
"""
3620. Network Recovery Pathways
Time: O((n + m) log m)
Space: O(n + m)
"""
# n is the number of nodes; infer from online.
n = len(online)
INF = float("inf")
# Adjacency list and in-degrees for a single topological sort.
adj = [[] for _ in range(n)]
indeg = [0] * n
for u, v, c in edges:
adj[u].append((v, c))
indeg[v] += 1
# Kahn's topological order (computed once, reused across checks).
topo = []
dq = deque(i for i in range(n) if indeg[i] == 0)
deg = indeg[:]
while dq:
u = dq.popleft()
topo.append(u)
for v, _ in adj[u]:
deg[v] -= 1
if deg[v] == 0:
dq.append(v)
def feasible(x: int) -> bool:
# Min total cost from 0 to n-1 using edges with cost >= x
# and only online nodes.
dist = [INF] * n
dist[0] = 0
for u in topo:
du = dist[u]
if du == INF or not online[u]:
continue
for v, c in adj[u]:
if c >= x and online[v] and du + c < dist[v]:
dist[v] = du + c
return dist[n - 1] <= k
costs = sorted({c for _, _, c in edges})
if not costs or not feasible(costs[0]):
return -1
# Largest candidate cost x with feasible(x).
lo, hi, ans = 0, len(costs) - 1, -1
while lo <= hi:
mid = (lo + hi) // 2
if feasible(costs[mid]):
ans = costs[mid]
lo = mid + 1
else:
hi = mid - 1
return ansimport java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
/**
* 3620. Network Recovery Pathways
* Time: O((n + m) log m)
* Space: O(n + m)
*/
class Solution {
private int n;
private List<int[]>[] adj;
private int[] topo;
private boolean[] online;
private long k;
public int findMaxPathScore(int[][] edges, boolean[] online, long k) {
this.n = online.length;
this.online = online;
this.k = k;
adj = new List[n];
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<>();
}
int[] indeg = new int[n];
for (int[] e : edges) {
adj[e[0]].add(new int[]{e[1], e[2]});
indeg[e[1]]++;
}
// Kahn's topological order (computed once, reused across checks).
topo = new int[n];
int idx = 0;
ArrayDeque<Integer> dq = new ArrayDeque<>();
int[] deg = indeg.clone();
for (int i = 0; i < n; i++) {
if (deg[i] == 0) {
dq.add(i);
}
}
while (!dq.isEmpty()) {
int u = dq.poll();
topo[idx++] = u;
for (int[] nb : adj[u]) {
if (--deg[nb[0]] == 0) {
dq.add(nb[0]);
}
}
}
// Distinct sorted edge costs are the only candidate answers.
TreeSet<Integer> set = new TreeSet<>();
for (int[] e : edges) {
set.add(e[2]);
}
if (set.isEmpty() || !feasible(set.first())) {
return -1;
}
Integer[] costs = set.toArray(new Integer[0]);
int lo = 0, hi = costs.length - 1, ans = -1;
while (lo <= hi) {
int mid = (lo + hi) >>> 1;
if (feasible(costs[mid])) {
ans = costs[mid];
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
private boolean feasible(int x) {
long INF = Long.MAX_VALUE;
long[] dist = new long[n];
java.util.Arrays.fill(dist, INF);
dist[0] = 0;
for (int u : topo) {
long du = dist[u];
if (du == INF || !online[u]) {
continue;
}
for (int[] nb : adj[u]) {
int v = nb[0], c = nb[1];
if (c >= x && online[v] && du + c < dist[v]) {
dist[v] = du + c;
}
}
}
return dist[n - 1] <= k;
}
}