2492. Minimum Score of a Path Between Two Cities - Explanation
Description
Cities are labeled 1 through n. Each entry [a, b, distance] in roads is a bidirectional road of the given distance between cities a and b.
The score of a route is the smallest road distance encountered along it. Return the minimum possible score of a route from city 1 to city n.
You may revisit any city or reuse any road, including cities 1 and n. At least one route from 1 to n exists, but other cities may be disconnected.
Example 1:
Input: n = 4
roads = [[1,2,8],[2,4,7],[2,3,2]]
Output: 2Explanation: Take the route 1 → 2 → 3 → 2 → 4. Its road distances are 8, 2, 2, and 7, so its score is 2. Reusing the road between 2 and 3 is allowed.
Example 2:
Input: n = 5
roads = [[1,5,9],[2,3,1]]
Output: 9Explanation: The only route from 1 to 5 uses their road of distance 9. The road of distance 1 is in a disconnected component and cannot be included.
Example 3:
Input: n = 3
roads = [[1,2,6],[2,3,4],[1,3,10]]
Output: 4Explanation: The route 1 → 2 → 3 has score min(6, 4) = 4. No road has a smaller distance, so 4 is the minimum possible score.
Constraints:
2 <= n <= 1000001 <= roads.length <= 100000roads[i].length == 3- Each road connects distinct city labels between
1andn. 1 <= distance <= 10000- No bidirectional road appears more than once.
- At least one route connects city
1to cityn.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Graph Representation (Adjacency List) - Building and traversing graphs using adjacency lists to store edges and neighbors
- Depth First Search (DFS) - Recursively exploring all reachable nodes in a graph while tracking visited nodes
- Breadth First Search (BFS) - Level-by-level graph traversal using a queue
- Union-Find (Disjoint Set Union) - Efficiently grouping nodes into connected components with path compression and union by rank
1. Depth First Search
Intuition
The problem asks for the minimum edge weight on any path from city 1 to city n, where we can revisit edges and nodes. The key realization is that since we can traverse any edge multiple times, we're essentially looking for the minimum edge weight in the entire connected component containing city 1. If an edge is reachable from city 1, we can always include it in our path by going there and back.
Algorithm
- Build an adjacency list from the edges, storing both the neighbor and the edge distance.
- Initialize
resto infinity and a visited set. - Run DFS starting from node
1:- Mark the current node as visited.
- For each neighbor, update
reswith the minimum ofresand the edge distance. - Recursively visit unvisited neighbors.
- Return
resafter DFS completes.
class Solution:
def minScore(self, n: int, roads: list[list[int]]) -> int:
adj = defaultdict(list)
for src, dst, dist in roads:
adj[src].append((dst, dist))
adj[dst].append((src, dist))
res = float("inf")
visit = set()
def dfs(node):
nonlocal res
if node in visit:
return
visit.add(node)
for nei, dist in adj[node]:
res = min(res, dist)
dfs(nei)
dfs(1)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
2. Breadth First Search
Intuition
BFS achieves the same goal as DFS by exploring all reachable nodes level by level. Since we need to find the minimum edge weight in the connected component containing node 1, BFS works equally well. We process each node, check all its edges, and track the smallest weight seen.
Algorithm
- Build an adjacency list storing neighbors and distances.
- Initialize
resto infinity, a visited array, and a queue starting with node1. - While the queue is not empty:
- Dequeue a node.
- For each neighbor, update
reswith the minimum edge distance. - If the neighbor hasn't been visited, mark it visited and enqueue it.
- Return
res.
class Solution:
def minScore(self, n: int, roads: list[list[int]]) -> int:
adj = [[] for _ in range(n + 1)]
for src, dst, dist in roads:
adj[src].append((dst, dist))
adj[dst].append((src, dist))
res = float("inf")
visit = [False] * (n + 1)
q = deque([1])
visit[1] = True
while q:
node = q.popleft()
for nei, dist in adj[node]:
res = min(res, dist)
if not visit[nei]:
visit[nei] = True
q.append(nei)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
3. Iterative DFS
Intuition
This is the same approach as recursive DFS, but using an explicit stack instead of the call stack. This avoids potential stack overflow issues for very deep graphs and gives us more control over the traversal.
Algorithm
- Build an adjacency list from the edges.
- Initialize
resto infinity, a visited array, and a stack with node1. - While the stack is not empty:
- Pop a node from the stack.
- For each neighbor, update
reswith the minimum edge distance. - If the neighbor hasn't been visited, mark it visited and push it onto the stack.
- Return
res.
class Solution:
def minScore(self, n: int, roads: list[list[int]]) -> int:
adj = [[] for _ in range(n + 1)]
for src, dst, dist in roads:
adj[src].append((dst, dist))
adj[dst].append((src, dist))
res = float("inf")
visit = [False] * (n + 1)
stack = [1]
visit[1] = True
while stack:
node = stack.pop()
for nei, dist in adj[node]:
res = min(res, dist)
if not visit[nei]:
visit[nei] = True
stack.append(nei)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
4. Disjoint Set Union
Intuition
Union-Find provides another way to identify which edges belong to the same connected component as node 1. First, we union all nodes connected by edges. Then, we iterate through all edges and check if they belong to the same component as node 1. The minimum weight among those edges is our answer.
Algorithm
- Initialize a DSU (Disjoint Set Union) structure with path compression and union by size.
- For each edge, union the two endpoints.
- Find the root of node
1. - Iterate through all edges:
- If either endpoint has the same root as node
1, updatereswith the minimum edge weight.
- If either endpoint has the same root as node
- Return
res.
class DSU:
def __init__(self, n):
self.Parent = list(range(n + 1))
self.Size = [1] * (n + 1)
def find(self, node):
if self.Parent[node] != node:
self.Parent[node] = self.find(self.Parent[node])
return self.Parent[node]
def union(self, u, v):
pu, pv = self.find(u), self.find(v)
if pu == pv:
return False
if self.Size[pu] >= self.Size[pv]:
self.Size[pu] += self.Size[pv]
self.Parent[pv] = pu
else:
self.Size[pv] += self.Size[pu]
self.Parent[pu] = pv
return True
class Solution:
def minScore(self, n: int, roads: list[list[int]]) -> int:
dsu = DSU(n)
for src, dst, _ in roads:
dsu.union(src, dst)
res = float("inf")
root = dsu.find(1)
for src, dst, dist in roads:
if dsu.find(src) == root:
res = min(res, dist)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges in the graph. is used for amortized complexity.
Common Pitfalls
Treating It as a Shortest Path Problem
Many people instinctively reach for Dijkstra's algorithm when they see "minimum" and "path" in the same problem. However, this problem asks for the minimum edge weight along any path, not the shortest total distance. Since you can revisit edges freely, you simply need to find the smallest edge weight in the entire connected component containing node 1.
Only Considering Direct Paths
Some solutions only examine edges that lie on simple paths from node 1 to node n. This misses the key insight that you can traverse any edge in the connected component by taking detours. The answer is the minimum weight among all edges reachable from node 1, regardless of whether they appear on a direct path to node n.
Forgetting to Build Bidirectional Edges
The graph is undirected, so each edge must be added in both directions when constructing the adjacency list. Failing to do this will cause your traversal to miss large portions of the graph, leading to incorrect results when the minimum edge is only reachable through a path that requires traversing an edge in the "reverse" direction.
Sign in to join the discussion