LeetCode #3620 — HARD

Network Recovery Pathways

Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.

Solve on LeetCode
The Problem

Problem Statement

You are given a directed acyclic graph of n nodes numbered from 0 to n − 1. This is represented by a 2D array edges of length m, where edges[i] = [ui, vi, costi] indicates a one‑way communication from node ui to node vi with a recovery cost of costi.

Some nodes may be offline. You are given a boolean array online where online[i] = true means node i is online. Nodes 0 and n − 1 are always online.

A path from 0 to n − 1 is valid if:

  • All intermediate nodes on the path are online.
  • The total recovery cost of all edges on the path does not exceed k.

For each valid path, define its score as the minimum edge‑cost along that path.

Return the maximum path score (i.e., the largest minimum-edge cost) among all valid paths. If no valid path exists, return -1.

Example 1:

Input: edges = [[0,1,5],[1,3,10],[0,2,3],[2,3,4]], online = [true,true,true,true], k = 10

Output: 3

Explanation:

  • The graph has two possible routes from node 0 to node 3:

    1. Path 0 → 1 → 3

      • Total cost = 5 + 10 = 15, which exceeds k (15 > 10), so this path is invalid.

    2. Path 0 → 2 → 3

      • Total cost = 3 + 4 = 7 <= k, so this path is valid.

      • The minimum edge‐cost along this path is min(3, 4) = 3.

  • There are no other valid paths. Hence, the maximum among all valid path‐scores is 3.

Example 2:

Input: 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

Output: 6

Explanation:

  • Node 3 is offline, so any path passing through 3 is invalid.

  • Consider the remaining routes from 0 to 4:

    1. Path 0 → 1 → 4

      • Total cost = 7 + 5 = 12 <= k, so this path is valid.

      • The minimum edge‐cost along this path is min(7, 5) = 5.

    2. Path 0 → 2 → 3 → 4

      • Node 3 is offline, so this path is invalid regardless of cost.

    3. Path 0 → 2 → 4

      • Total cost = 6 + 6 = 12 <= k, so this path is valid.

      • The minimum edge‐cost along this path is min(6, 6) = 6.

  • Among the two valid paths, their scores are 5 and 6. Therefore, the answer is 6.

Constraints:

  • n == online.length
  • 2 <= n <= 5 * 104
  • 0 <= m == edges.length <= min(105, n * (n - 1) / 2)
  • edges[i] = [ui, vi, costi]
  • 0 <= ui, vi < n
  • ui != vi
  • 0 <= costi <= 109
  • 0 <= k <= 5 * 1013
  • online[i] is either true or false, and both online[0] and online[n − 1] are true.
  • The given graph is a directed acyclic graph.
Patterns Used

Roadmap

  1. Brute Force Baseline
  2. Core Insight
  3. Algorithm Walkthrough
  4. Edge Cases
  5. Full Annotated Code
  6. Interactive Study Demo
  7. Complexity Analysis
Step 01

Brute Force Baseline

Problem summary: You are given a directed acyclic graph of n nodes numbered from 0 to n − 1. This is represented by a 2D array edges of length m, where edges[i] = [ui, vi, costi] indicates a one‑way communication from node ui to node vi with a recovery cost of costi. Some nodes may be offline. You are given a boolean array online where online[i] = true means node i is online. Nodes 0 and n − 1 are always online. A path from 0 to n − 1 is valid if: All intermediate nodes on the path are online. The total recovery cost of all edges on the path does not exceed k. For each valid path, define its score as the minimum edge‑cost along that path. Return the maximum path score (i.e., the largest minimum-edge cost) among all valid paths. If no valid path exists, return -1.

Baseline thinking

Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.

Pattern signal: Array · Binary Search · Dynamic Programming · Topological Sort

Example 1

[[0,1,5],[1,3,10],[0,2,3],[2,3,4]]
[true,true,true,true]
10

Example 2

[[0,1,7],[1,4,5],[0,2,6],[2,3,6],[3,4,2],[2,4,6]]
[true,true,true,false,true]
12
Step 02

Core Insight

What unlocks the optimal approach

  • Use binary search on <code>ans</code>.
  • Check if a particular <code>ans</code> is possible by including only the edges with weights ≥ <code>mid</code> (the current binary‐search pivot).
  • Implement the check function using either <code>Dijkstra</code> or DP (via topological sorting, since the graph is a DAG).
Interview move: turn each hint into an invariant you can check after every iteration/recursion step.
Step 03

Algorithm Walkthrough

Iteration Checklist

  1. Define state (indices, window, stack, map, DP cell, or recursion frame).
  2. Apply one transition step and update the invariant.
  3. Record answer candidate when condition is met.
  4. Continue until all input is consumed.
Use the first example testcase as your mental trace to verify each transition.
Step 04

Edge Cases

Minimum Input
Single element / shortest valid input
Validate boundary behavior before entering the main loop or recursion.
Duplicates & Repeats
Repeated values / repeated states
Decide whether duplicates should be merged, skipped, or counted explicitly.
Extreme Constraints
Largest constraint values
Re-check complexity target against constraints to avoid time-limit issues.
Invalid / Corner Shape
Empty collections, zeros, or disconnected structures
Handle special-case structure before the core algorithm path.
Step 05

Full Annotated Code

Source-backed implementations are provided below for direct study and interview prep.

// Accepted solution for LeetCode #3620: Network Recovery Pathways
// Auto-generated Java example from go.
class Solution {
    public void exampleSolution() {
    }
}
// Reference (go):
// // Accepted solution for LeetCode #3620: Network Recovery Pathways
// package main
// 
// import (
// 	"math"
// 	"slices"
// 	"sort"
// )
// 
// // https://space.bilibili.com/206214
// func findMaxPathScore1(edges [][]int, online []bool, k int64) int {
// 	n := len(online)
// 	type edge struct{ to, wt int }
// 	g := make([][]edge, n)
// 	maxWt := -1
// 	for _, e := range edges {
// 		x, y, wt := e[0], e[1], e[2]
// 		if online[x] && online[y] {
// 			g[x] = append(g[x], edge{y, wt})
// 			if x == 0 {
// 				maxWt = max(maxWt, wt)
// 			}
// 		}
// 	}
// 
// 	memo := make([]int, n)
// 	// 二分无法到达 n-1 的最小 lower,那么减一后,就是可以到达 n-1 的最大 lower
// 	ans := sort.Search(maxWt+1, func(lower int) bool {
// 		for i := range memo {
// 			memo[i] = -1
// 		}
// 		var dfs func(int) int
// 		dfs = func(x int) int {
// 			if x == n-1 { // 到达终点
// 				return 0
// 			}
// 			p := &memo[x]
// 			if *p != -1 { // 之前计算过
// 				return *p
// 			}
// 			res := math.MaxInt / 2 // 防止加法溢出
// 			for _, e := range g[x] {
// 				y := e.to
// 				if e.wt >= lower {
// 					res = min(res, dfs(y)+e.wt)
// 				}
// 			}
// 			*p = res // 记忆化
// 			return res
// 		}
// 		return dfs(0) > int(k)
// 	}) - 1
// 	return ans
// }
// 
// func findMaxPathScore(edges [][]int, online []bool, k int64) int {
// 	n := len(online)
// 	type edge struct{ to, wt int }
// 	g := make([][]edge, n)
// 	deg := make([]int, n)
// 	maxWt := 0
// 	for _, e := range edges {
// 		x, y, wt := e[0], e[1], e[2]
// 		if online[x] && online[y] {
// 			g[x] = append(g[x], edge{y, wt})
// 			deg[y]++
// 			maxWt = max(maxWt, wt)
// 		}
// 	}
// 
// 	// 先清理无法从 0 到达的边,比如 2 -> 0 
// 	q := []int{}
// 	for i := 1; i < n; i++ {
// 		if deg[i] == 0 {
// 			q = append(q, i)
// 		}
// 	}
// 	for len(q) > 0 {
// 		x := q[0]
// 		q = q[1:]
// 		for _, e := range g[x] {
// 			y := e.to
// 			deg[y]--
// 			if deg[y] == 0 && y > 0 {
// 				q = append(q, y)
// 			}
// 		}
// 	}
// 
// 	f := make([]int, n)
// 	ans := sort.Search(maxWt+1, func(lower int) bool {
// 		deg := slices.Clone(deg)
// 		for i := 1; i < n; i++ {
// 			f[i] = math.MaxInt / 2
// 		}
// 
// 		q := []int{0}
// 		for len(q) > 0 {
// 			x := q[0]
// 			if x == n-1 {
// 				return f[x] > int(k)
// 			}
// 			q = q[1:]
// 			for _, e := range g[x] {
// 				y := e.to
// 				wt := e.wt
// 				if wt >= lower {
// 					f[y] = min(f[y], f[x]+wt)
// 				}
// 				deg[y]--
// 				if deg[y] == 0 {
// 					q = append(q, y)
// 				}
// 			}
// 		}
// 		return true
// 	}) - 1
// 	return ans
// }
Step 06

Interactive Study Demo

Use this to step through a reusable interview workflow for this problem.

Press Step or Run All to begin.
Step 07

Complexity Analysis

Time
O(log n)
Space
O(1)

Approach Breakdown

LINEAR SCAN
O(n) time
O(1) space

Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.

BINARY SEARCH
O(log n) time
O(1) space

Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).

Shortcut: Halving the input each step → O(log n). Works on any monotonic condition, not just sorted arrays.
Coach Notes

Common Mistakes

Review these before coding to avoid predictable interview regressions.

Off-by-one on range boundaries

Wrong move: Loop endpoints miss first/last candidate.

Usually fails on: Fails on minimal arrays and exact-boundary answers.

Fix: Re-derive loops from inclusive/exclusive ranges before coding.

Boundary update without `+1` / `-1`

Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.

Usually fails on: Two-element ranges never converge.

Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.

State misses one required dimension

Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.

Usually fails on: Correctness breaks on cases that differ only in hidden state.

Fix: Define state so each unique subproblem maps to one DP cell.