2959. Number of Possible Sets of Closing Branches
Problem Description
You have a company with n
branches (numbered from 0 to n-1) connected by roads. Initially, all branches can reach each other through these roads.
The company wants to reduce travel time by potentially closing some branches. After closing branches, they need to ensure that among the remaining open branches, the maximum distance between any two branches doesn't exceed maxDistance
.
Given:
n
: the number of branchesmaxDistance
: the maximum allowed distance between any two remaining branchesroads
: a 2D array whereroads[i] = [ui, vi, wi]
represents an undirected road between branchui
and branchvi
with lengthwi
The distance between two branches is defined as the minimum total length needed to travel from one to the other.
When a branch is closed, all roads connected to it become inaccessible.
Your task is to find the total number of different sets of branches that can remain open (including the possibility of keeping all branches open or closing all branches) such that the distance between any two open branches is at most maxDistance
.
Note that there can be multiple roads between the same pair of branches.
For example, if you have 3 branches and decide to keep branches 0 and 2 open while closing branch 1, then you need to check if the distance between branches 0 and 2 (using only roads that don't involve branch 1) is at most maxDistance
.
Intuition
The key observation is that n ≤ 10
, which means we have at most 10 branches. This small constraint immediately suggests that we can try all possible combinations of which branches to keep open.
Since we have n
branches, and each branch can either be open or closed, we have 2^n
possible configurations. With n ≤ 10
, this gives us at most 2^10 = 1024
possibilities, which is computationally feasible to check all of them.
For each possible subset of branches that we keep open, we need to verify if the maximum distance between any two open branches is within maxDistance
. To find the shortest distances between all pairs of branches, we can use the Floyd-Warshall algorithm.
The approach becomes:
- Use binary enumeration to generate all possible subsets of branches (represented by a bitmask from
0
to2^n - 1
) - For each subset (mask), build a distance graph considering only the roads between open branches
- Apply Floyd-Warshall to find shortest paths between all pairs of open branches
- Check if all distances between open branches are at most
maxDistance
- Count how many valid subsets satisfy the condition
The binary mask representation is particularly elegant here: if bit i
is set in the mask, it means branch i
is open. For example, mask 101
(binary) means branches 0 and 2 are open while branch 1 is closed.
When building the graph for each subset, we only include roads where both endpoints are open branches (mask >> u & 1
and mask >> v & 1
both equal 1). This naturally handles the constraint that closed branches and their connected roads become inaccessible.
Learn more about Graph, Shortest Path and Heap (Priority Queue) patterns.
Solution Approach
The solution uses binary enumeration combined with the Floyd-Warshall algorithm to find all valid sets of branches.
Step 1: Binary Enumeration
We iterate through all possible subsets using a mask from 0
to 2^n - 1
. Each bit position in the mask represents whether a branch is open (1) or closed (0).
for mask in range(1 << n):
Step 2: Build Distance Graph
For each subset, we initialize a distance matrix g
with infinity values. We then populate it with roads that connect only open branches:
g = [[inf] * n for _ in range(n)]
for u, v, w in roads:
if mask >> u & 1 and mask >> v & 1: # Both branches are open
g[u][v] = min(g[u][v], w)
g[v][u] = min(g[v][u], w)
The condition mask >> u & 1
checks if branch u
is open by right-shifting the mask by u
positions and checking the least significant bit. We use min()
to handle multiple roads between the same branches.
Step 3: Floyd-Warshall Algorithm We apply Floyd-Warshall to find shortest paths between all pairs of open branches:
for k in range(n):
if mask >> k & 1: # Only use open branches as intermediate nodes
g[k][k] = 0
for i in range(n):
for j in range(n):
if g[i][k] + g[k][j] < g[i][j]:
g[i][j] = g[i][k] + g[k][j]
The algorithm considers each branch k
as an intermediate point and updates g[i][j]
if a shorter path exists through k
. We only use open branches as intermediate nodes.
Step 4: Validate the Subset
After computing all shortest distances, we check if all distances between open branches are within maxDistance
:
if all(
g[i][j] <= maxDistance
for i in range(n)
for j in range(n)
if mask >> i & 1 and mask >> j & 1
):
ans += 1
We only check distances between branches that are actually open in the current subset.
Time Complexity: O(2^n * n^3)
where 2^n
is for enumerating all subsets and n^3
is for Floyd-Warshall on each subset.
Space Complexity: O(n^2)
for the distance matrix.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Example:
n = 3
branches (labeled 0, 1, 2)maxDistance = 5
roads = [[0, 1, 2], [1, 2, 3], [0, 2, 7]]
This creates a triangle where:
- Branch 0 to 1: distance 2
- Branch 1 to 2: distance 3
- Branch 0 to 2: distance 7
Step 1: Enumerate all possible subsets
We'll check all 8 possible masks (000 to 111 in binary):
Mask = 0 (binary: 000) - All branches closed
- No open branches, so trivially valid
- Count: 1
Mask = 1 (binary: 001) - Only branch 0 open
- Single branch, no pairs to check
- Count: 2
Mask = 2 (binary: 010) - Only branch 1 open
- Single branch, no pairs to check
- Count: 3
Mask = 3 (binary: 011) - Branches 0 and 1 open
- Build graph with only roads between 0 and 1
- Distance[0][1] = 2 (from road [0,1,2])
- Check: 2 ≤ 5? Yes, valid
- Count: 4
Mask = 4 (binary: 100) - Only branch 2 open
- Single branch, no pairs to check
- Count: 5
Mask = 5 (binary: 101) - Branches 0 and 2 open
- Build graph with only roads between 0 and 2
- Distance[0][2] = 7 (from road [0,2,7])
- Check: 7 ≤ 5? No, invalid
- Count: 5 (unchanged)
Mask = 6 (binary: 110) - Branches 1 and 2 open
- Build graph with only roads between 1 and 2
- Distance[1][2] = 3 (from road [1,2,3])
- Check: 3 ≤ 5? Yes, valid
- Count: 6
Mask = 7 (binary: 111) - All branches open
- Build complete graph with all roads
- Initial distances:
- Distance[0][1] = 2
- Distance[1][2] = 3
- Distance[0][2] = 7
- Apply Floyd-Warshall:
- Using branch 1 as intermediate: Distance[0][2] = min(7, 2+3) = 5
- Final distances after Floyd-Warshall:
- Distance[0][1] = 2
- Distance[1][2] = 3
- Distance[0][2] = 5
- Check all pairs: max(2, 3, 5) = 5 ≤ 5? Yes, valid
- Count: 7
Final Answer: 7
The key insight demonstrated here is that mask 5 (branches 0 and 2 open) is invalid because the direct distance between them (7) exceeds maxDistance. However, mask 7 (all branches open) is valid because we can travel from branch 0 to 2 via branch 1, giving us a shorter path of length 5.
Solution Implementation
1from typing import List
2from math import inf
3
4
5class Solution:
6 def numberOfSets(self, n: int, maxDistance: int, roads: List[List[int]]) -> int:
7 """
8 Count the number of valid subsets of nodes where all pairwise distances
9 are at most maxDistance.
10
11 Args:
12 n: Number of nodes in the graph
13 maxDistance: Maximum allowed distance between any two nodes in a valid subset
14 roads: List of edges [u, v, weight] representing undirected roads
15
16 Returns:
17 Number of valid subsets
18 """
19 valid_subset_count = 0
20
21 # Iterate through all possible subsets using bit masking
22 # Each bit position i represents whether node i is included in the subset
23 for subset_mask in range(1 << n):
24 # Initialize distance matrix with infinity
25 distance_matrix = [[inf] * n for _ in range(n)]
26
27 # Build adjacency matrix for nodes in current subset
28 for u, v, weight in roads:
29 # Check if both endpoints are in the current subset
30 if (subset_mask >> u) & 1 and (subset_mask >> v) & 1:
31 # Update with minimum weight (handle multiple edges between same nodes)
32 distance_matrix[u][v] = min(distance_matrix[u][v], weight)
33 distance_matrix[v][u] = min(distance_matrix[v][u], weight)
34
35 # Floyd-Warshall algorithm to find shortest paths
36 for intermediate_node in range(n):
37 # Only use nodes that are in the current subset as intermediate nodes
38 if (subset_mask >> intermediate_node) & 1:
39 # Distance from a node to itself is 0
40 distance_matrix[intermediate_node][intermediate_node] = 0
41
42 # Update shortest paths through intermediate_node
43 for start_node in range(n):
44 for end_node in range(n):
45 # Relax the distance if a shorter path exists through intermediate_node
46 if distance_matrix[start_node][intermediate_node] + distance_matrix[intermediate_node][end_node] < distance_matrix[start_node][end_node]:
47 distance_matrix[start_node][end_node] = distance_matrix[start_node][intermediate_node] + distance_matrix[intermediate_node][end_node]
48
49 # Check if all pairwise distances in subset satisfy the maxDistance constraint
50 is_valid_subset = all(
51 distance_matrix[i][j] <= maxDistance
52 for i in range(n)
53 for j in range(n)
54 if (subset_mask >> i) & 1 and (subset_mask >> j) & 1
55 )
56
57 if is_valid_subset:
58 valid_subset_count += 1
59
60 return valid_subset_count
61
1class Solution {
2 public int numberOfSets(int n, int maxDistance, int[][] roads) {
3 int validSubsets = 0;
4
5 // Iterate through all possible subsets of nodes using bitmask
6 for (int mask = 0; mask < (1 << n); ++mask) {
7 // Initialize adjacency matrix with large values (representing infinity)
8 int[][] distanceMatrix = new int[n][n];
9 for (int[] row : distanceMatrix) {
10 Arrays.fill(row, 1 << 29);
11 }
12
13 // Build graph with only nodes that are in the current subset
14 for (int[] road : roads) {
15 int nodeU = road[0];
16 int nodeV = road[1];
17 int weight = road[2];
18
19 // Check if both nodes are in the current subset
20 if ((mask >> nodeU & 1) == 1 && (mask >> nodeV & 1) == 1) {
21 // Update with minimum weight (handle multiple edges between same nodes)
22 distanceMatrix[nodeU][nodeV] = Math.min(distanceMatrix[nodeU][nodeV], weight);
23 distanceMatrix[nodeV][nodeU] = Math.min(distanceMatrix[nodeV][nodeU], weight);
24 }
25 }
26
27 // Apply Floyd-Warshall algorithm to find shortest paths
28 for (int intermediate = 0; intermediate < n; ++intermediate) {
29 // Only use nodes that are in the current subset as intermediate nodes
30 if ((mask >> intermediate & 1) == 1) {
31 // Distance from a node to itself is 0
32 distanceMatrix[intermediate][intermediate] = 0;
33
34 // Update shortest paths through intermediate node
35 for (int source = 0; source < n; ++source) {
36 for (int destination = 0; destination < n; ++destination) {
37 distanceMatrix[source][destination] = Math.min(
38 distanceMatrix[source][destination],
39 distanceMatrix[source][intermediate] + distanceMatrix[intermediate][destination]
40 );
41 }
42 }
43 }
44 }
45
46 // Check if all pairs of nodes in subset satisfy the maxDistance constraint
47 int isValidSubset = 1;
48 for (int i = 0; i < n && isValidSubset == 1; ++i) {
49 for (int j = 0; j < n && isValidSubset == 1; ++j) {
50 // Check only pairs where both nodes are in the current subset
51 if ((mask >> i & 1) == 1 && (mask >> j & 1) == 1) {
52 if (distanceMatrix[i][j] > maxDistance) {
53 isValidSubset = 0;
54 }
55 }
56 }
57 }
58
59 // Add to count if this subset is valid
60 validSubsets += isValidSubset;
61 }
62
63 return validSubsets;
64 }
65}
66
1class Solution {
2public:
3 int numberOfSets(int n, int maxDistance, vector<vector<int>>& roads) {
4 int validSubsetCount = 0;
5
6 // Try all possible subsets of nodes using bitmask
7 for (int nodeMask = 0; nodeMask < (1 << n); ++nodeMask) {
8 // Initialize distance matrix with large values
9 const int INF = 0x3f3f3f3f;
10 int distanceMatrix[n][n];
11 memset(distanceMatrix, 0x3f, sizeof(distanceMatrix));
12
13 // Build adjacency matrix for nodes in current subset
14 for (const auto& road : roads) {
15 int nodeU = road[0];
16 int nodeV = road[1];
17 int weight = road[2];
18
19 // Only add edge if both nodes are in the current subset
20 if ((nodeMask >> nodeU & 1) && (nodeMask >> nodeV & 1)) {
21 distanceMatrix[nodeU][nodeV] = min(distanceMatrix[nodeU][nodeV], weight);
22 distanceMatrix[nodeV][nodeU] = min(distanceMatrix[nodeV][nodeU], weight);
23 }
24 }
25
26 // Floyd-Warshall algorithm to find shortest paths between all pairs
27 for (int intermediateNode = 0; intermediateNode < n; ++intermediateNode) {
28 // Only use nodes that are in the current subset as intermediate nodes
29 if (nodeMask >> intermediateNode & 1) {
30 // Distance from a node to itself is 0
31 distanceMatrix[intermediateNode][intermediateNode] = 0;
32
33 for (int sourceNode = 0; sourceNode < n; ++sourceNode) {
34 for (int targetNode = 0; targetNode < n; ++targetNode) {
35 // Update shortest path using intermediate node
36 distanceMatrix[sourceNode][targetNode] = min(
37 distanceMatrix[sourceNode][targetNode],
38 distanceMatrix[sourceNode][intermediateNode] +
39 distanceMatrix[intermediateNode][targetNode]
40 );
41 }
42 }
43 }
44 }
45
46 // Check if all distances in subset satisfy maxDistance constraint
47 bool isValidSubset = true;
48 for (int i = 0; i < n && isValidSubset; ++i) {
49 for (int j = 0; j < n && isValidSubset; ++j) {
50 // Check distance only between nodes that are both in the subset
51 if ((nodeMask >> i & 1) && (nodeMask >> j & 1)) {
52 if (distanceMatrix[i][j] > maxDistance) {
53 isValidSubset = false;
54 }
55 }
56 }
57 }
58
59 // Increment count if this subset is valid
60 if (isValidSubset) {
61 validSubsetCount++;
62 }
63 }
64
65 return validSubsetCount;
66 }
67};
68
1/**
2 * Counts the number of valid subsets of nodes where all pairs of nodes
3 * in the subset are connected within the maximum distance constraint
4 * @param n - Number of nodes in the graph
5 * @param maxDistance - Maximum allowed distance between any two nodes in a valid subset
6 * @param roads - Array of edges where each edge is [u, v, weight]
7 * @returns Number of valid subsets
8 */
9function numberOfSets(n: number, maxDistance: number, roads: number[][]): number {
10 let validSubsetCount = 0;
11
12 // Iterate through all possible subsets of nodes using bit masking
13 // Each bit in mask represents whether a node is included (1) or excluded (0)
14 for (let mask = 0; mask < (1 << n); ++mask) {
15 // Initialize adjacency matrix with infinity for all pairs
16 const distanceMatrix: number[][] = Array.from(
17 { length: n },
18 () => Array(n).fill(Infinity)
19 );
20
21 // Build the graph considering only nodes included in current subset
22 for (const [nodeU, nodeV, weight] of roads) {
23 // Check if both endpoints are in the current subset
24 if (((mask >> nodeU) & 1) && ((mask >> nodeV) & 1)) {
25 // Update with minimum weight for multiple edges between same nodes
26 distanceMatrix[nodeU][nodeV] = Math.min(distanceMatrix[nodeU][nodeV], weight);
27 distanceMatrix[nodeV][nodeU] = Math.min(distanceMatrix[nodeV][nodeU], weight);
28 }
29 }
30
31 // Apply Floyd-Warshall algorithm to find shortest paths between all pairs
32 for (let intermediateNode = 0; intermediateNode < n; ++intermediateNode) {
33 // Only use nodes that are in the current subset as intermediate nodes
34 if ((mask >> intermediateNode) & 1) {
35 // Distance from a node to itself is 0
36 distanceMatrix[intermediateNode][intermediateNode] = 0;
37
38 // Update shortest paths through the intermediate node
39 for (let startNode = 0; startNode < n; ++startNode) {
40 for (let endNode = 0; endNode < n; ++endNode) {
41 distanceMatrix[startNode][endNode] = Math.min(
42 distanceMatrix[startNode][endNode],
43 distanceMatrix[startNode][intermediateNode] +
44 distanceMatrix[intermediateNode][endNode]
45 );
46 }
47 }
48 }
49 }
50
51 // Validate if all pairs in the subset satisfy the distance constraint
52 let isValidSubset = 1;
53 for (let i = 0; i < n && isValidSubset; ++i) {
54 for (let j = 0; j < n && isValidSubset; ++j) {
55 // Check if both nodes are in subset and their distance exceeds limit
56 if (((mask >> i) & 1) && ((mask >> j) & 1) &&
57 distanceMatrix[i][j] > maxDistance) {
58 isValidSubset = 0;
59 }
60 }
61 }
62
63 // Increment count if this subset is valid
64 validSubsetCount += isValidSubset;
65 }
66
67 return validSubsetCount;
68}
69
Time and Space Complexity
Time Complexity: O(2^n × (n^3 + m))
The algorithm iterates through all possible subsets of nodes using a bitmask, which gives us 2^n
iterations in the outer loop. For each subset (mask):
-
Building the adjacency matrix takes
O(m)
time, where we iterate through all roads and update the graph only if both endpoints are in the current subset. -
Floyd-Warshall algorithm is applied to find all-pairs shortest paths, which has three nested loops each running up to
n
times, resulting inO(n^3)
time complexity. -
The final validation check iterates through all pairs of nodes in the subset, which in the worst case is
O(n^2)
, but this is dominated by the Floyd-Warshall step.
Therefore, the total time complexity is O(2^n × (n^3 + m))
, where n
is the number of nodes and m
is the number of roads.
Space Complexity: O(n^2)
The space is dominated by the 2D adjacency matrix g
which stores the distances between all pairs of nodes. This matrix has dimensions n × n
, requiring O(n^2)
space. The other variables (ans
, mask
, loop indices) use constant space, so the overall space complexity is O(n^2)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Set Self-Distance to Zero
Pitfall: A critical issue occurs when we don't explicitly set distance_matrix[k][k] = 0
for nodes in the subset. Without this, the distance from a node to itself remains inf
, causing the Floyd-Warshall algorithm to produce incorrect results and potentially rejecting valid subsets.
Why it happens: When initializing the distance matrix with inf
, we might assume Floyd-Warshall will naturally handle self-distances, but it won't without explicit initialization.
Example of the bug:
# WRONG - Missing self-distance initialization
for intermediate_node in range(n):
if (subset_mask >> intermediate_node) & 1:
# distance_matrix[intermediate_node][intermediate_node] = 0 # MISSING!
for start_node in range(n):
for end_node in range(n):
# This will fail because self-distances are still inf
if distance_matrix[start_node][intermediate_node] + distance_matrix[intermediate_node][end_node] < distance_matrix[start_node][end_node]:
distance_matrix[start_node][end_node] = ...
Solution: Always set diagonal elements to 0 for nodes in the current subset before running Floyd-Warshall:
# CORRECT - Set self-distances first
for intermediate_node in range(n):
if (subset_mask >> intermediate_node) & 1:
distance_matrix[intermediate_node][intermediate_node] = 0 # Critical!
for start_node in range(n):
for end_node in range(n):
# Now the algorithm works correctly
2. Incorrect Bit Manipulation for Subset Checking
Pitfall: Using wrong bit operations like mask & (1 << u)
instead of (mask >> u) & 1
, or forgetting parentheses, leading to incorrect subset membership checks.
Example of the bug:
# WRONG - Various bit manipulation errors if mask & u: # Wrong: compares mask with u directly if mask >> u & 1 == 1: # Wrong: operator precedence issue (& binds tighter than ==) if mask << u & 1: # Wrong: shifts in wrong direction
Solution: Use the correct pattern with proper parentheses:
# CORRECT if (mask >> u) & 1: # Shifts mask right by u positions, then checks LSB
3. Not Handling Multiple Edges Between Same Nodes
Pitfall: Overwriting edge weights instead of taking the minimum when multiple roads exist between the same pair of branches.
Example of the bug:
# WRONG - Overwrites previous edge weight for u, v, weight in roads: if (subset_mask >> u) & 1 and (subset_mask >> v) & 1: distance_matrix[u][v] = weight # Overwrites! distance_matrix[v][u] = weight
Solution: Always use min()
to keep the shortest direct road:
# CORRECT - Keeps minimum weight
for u, v, weight in roads:
if (subset_mask >> u) & 1 and (subset_mask >> v) & 1:
distance_matrix[u][v] = min(distance_matrix[u][v], weight)
distance_matrix[v][u] = min(distance_matrix[v][u], weight)
4. Running Floyd-Warshall on All Nodes Instead of Subset Nodes
Pitfall: Using nodes outside the current subset as intermediate nodes in Floyd-Warshall, which can lead to incorrect distance calculations since those nodes are "closed."
Example of the bug:
# WRONG - Uses all nodes as intermediates
for k in range(n): # Should check if k is in subset!
for i in range(n):
for j in range(n):
distance_matrix[i][j] = min(distance_matrix[i][j],
distance_matrix[i][k] + distance_matrix[k][j])
Solution: Only use nodes in the current subset as intermediate nodes:
# CORRECT - Only uses subset nodes as intermediates
for k in range(n):
if (subset_mask >> k) & 1: # Check if k is in subset
distance_matrix[k][k] = 0
for i in range(n):
for j in range(n):
# Update distances
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Shortest Path Between A and B Prereq BFS on Graph problems graph_bfs Given an unweighted connected graph return the length of the shortest path between two nodes A and B in terms of the number of edges Assume there always exists a path between nodes A and B Input graph
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Want a Structured Path to Master System Design Too? Don’t Miss This!