2101. Detonate the Maximum Bombs
Problem Description
You have a list of bombs, where each bomb has a circular explosion range. When you detonate a bomb, it triggers all other bombs within its range, and those bombs will in turn trigger bombs within their ranges, creating a chain reaction.
Each bomb is represented as bombs[i] = [xi, yi, ri]
where:
(xi, yi)
is the location of the bomb on a 2D planeri
is the radius of the bomb's explosion range
A bomb at position (x1, y1)
with radius r1
can trigger another bomb at position (x2, y2)
if the distance between them is less than or equal to r1
. The distance is calculated using the Euclidean formula: β((x1-x2)Β² + (y1-y2)Β²)
.
You can choose to detonate exactly one bomb initially. Your goal is to find the maximum number of bombs that can be detonated (including the initial bomb and all bombs triggered through chain reactions) by choosing the optimal starting bomb.
For example, if bomb A triggers bomb B, and bomb B triggers bomb C, then detonating bomb A would result in 3 total detonations. The problem asks you to find which initial bomb choice gives you the maximum total detonations.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: This problem can be modeled as a directed graph where:
- Each bomb is a node
- There's a directed edge from bomb
i
to bombj
if bombi
can trigger bombj
(when the distance between them β€ radius of bombi
) - The chain reaction of detonations is essentially graph traversal
Is it a tree?
- No: The graph is not a tree because:
- It can have cycles (bomb A can trigger B, and B can trigger A if they're both within each other's range)
- A node can have multiple parents (multiple bombs can trigger the same bomb)
- It's a directed graph where edges are based on explosion ranges
Is the problem related to directed acyclic graphs?
- No: The graph can contain cycles as bombs can be within each other's explosion ranges
Is the problem related to shortest paths?
- No: We're not looking for the shortest path between nodes. Instead, we want to find the maximum number of nodes (bombs) reachable from a starting node
Does the problem involve connectivity?
- Yes: The core of the problem is about connectivity - finding how many bombs are connected (reachable) from a starting bomb through the chain reaction
Does the problem have small constraints?
- Yes: With typical constraints of n β€ 100 bombs, we can afford to run DFS/BFS from each bomb as a starting point
DFS/backtracking?
- Yes: DFS (or BFS as shown in the solution) is perfect for exploring all reachable nodes from a starting point
Conclusion: The flowchart suggests using DFS to explore the connectivity of the graph. We build a directed graph based on explosion ranges, then use DFS from each bomb to find the maximum number of bombs that can be detonated in a chain reaction.
Intuition
The key insight is recognizing that this is a graph traversal problem disguised as a geometry problem. When a bomb explodes, it triggers other bombs within its range, which then trigger more bombs - this is exactly how we traverse a graph from one node to its neighbors.
Think of each bomb as a node in a directed graph. If bomb A can trigger bomb B (meaning B is within A's explosion radius), we draw a directed edge from A to B. Notice that this relationship is not symmetric - bomb A might be able to trigger bomb B, but B might not be able to trigger A if B has a smaller radius.
To determine if one bomb can trigger another, we calculate the Euclidean distance between them: distance = β((x1-x2)Β² + (y1-y2)Β²)
. If this distance is less than or equal to the first bomb's radius, it can trigger the second bomb.
Once we've built this directed graph, the problem becomes: "Starting from which node can we reach the maximum number of nodes?" This is a classic graph traversal problem. We need to try starting from each bomb and see how many bombs we can reach through the chain reaction.
For each starting bomb, we perform a graph traversal (BFS or DFS) to visit all reachable bombs. We keep track of visited bombs to avoid counting the same bomb twice and to handle cycles in the graph. The traversal naturally simulates the chain reaction - as we visit each bomb, we explore all bombs it can trigger, then all bombs those can trigger, and so on.
The answer is the maximum count of bombs we can detonate across all possible starting choices. If at any point we can detonate all n bombs, we can return early since that's the maximum possible.
Learn more about Depth-First Search, Breadth-First Search, Graph and Math patterns.
Solution Approach
The solution uses BFS to traverse the graph and find the maximum number of bombs that can be detonated.
Step 1: Build the Directed Graph
First, we create an adjacency list g
where g[i]
contains all bombs that can be triggered by bomb i
. We iterate through all pairs of bombs and calculate the distance between them using the hypot
function (which computes β((x1-x2)Β² + (y1-y2)Β²)
).
For each pair of bombs i
and j
:
- If
dist <= r1
(distance is within bombi
's radius), bombi
can trigger bombj
, so we addj
tog[i]
- If
dist <= r2
(distance is within bombj
's radius), bombj
can trigger bombi
, so we addi
tog[j]
Note that we only check pairs where i < j
to avoid duplicate calculations, but we add edges in both directions when applicable.
Step 2: BFS from Each Starting Bomb
For each bomb k
as a potential starting point:
- Initialize a visited set
vis
with just bombk
- Create a queue
q
starting with bombk
- Process the queue using BFS:
- For each bomb
i
in the queue, explore all bombsj
ing[i]
(bombs thati
can trigger) - If bomb
j
hasn't been visited, mark it as visited and add it to the queue - This simulates the chain reaction spreading to all reachable bombs
- For each bomb
Step 3: Track the Maximum
After BFS completes for starting bomb k
, the size of vis
tells us how many bombs were detonated in total. We keep track of the maximum across all starting points.
Optimization: If at any point we can detonate all n
bombs (when len(vis) == n
), we immediately return n
since that's the maximum possible.
The time complexity is O(nΒ³)
where n
is the number of bombs: O(nΒ²)
to build the graph (checking all pairs) and O(nΒ²)
for BFS from each of the n
starting points. The space complexity is O(nΒ²)
for storing the adjacency list.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with 3 bombs:
- Bomb 0:
[1, 1, 2]
(position (1,1), radius 2) - Bomb 1:
[2, 2, 1]
(position (2,2), radius 1) - Bomb 2:
[5, 5, 3]
(position (5,5), radius 3)
Step 1: Build the Directed Graph
We calculate distances between all pairs:
-
Distance from Bomb 0 to Bomb 1:
β((1-2)Β² + (1-2)Β²) = β2 β 1.41
- Since 1.41 β€ 2 (Bomb 0's radius), Bomb 0 can trigger Bomb 1 β Add edge 0β1
- Since 1.41 > 1 (Bomb 1's radius), Bomb 1 cannot trigger Bomb 0
-
Distance from Bomb 0 to Bomb 2:
β((1-5)Β² + (1-5)Β²) = β32 β 5.66
- Since 5.66 > 2 (Bomb 0's radius), Bomb 0 cannot trigger Bomb 2
- Since 5.66 > 3 (Bomb 2's radius), Bomb 2 cannot trigger Bomb 0
-
Distance from Bomb 1 to Bomb 2:
β((2-5)Β² + (2-5)Β²) = β18 β 4.24
- Since 4.24 > 1 (Bomb 1's radius), Bomb 1 cannot trigger Bomb 2
- Since 4.24 > 3 (Bomb 2's radius), Bomb 2 cannot trigger Bomb 1
Our adjacency list becomes:
g[0] = [1] // Bomb 0 can trigger Bomb 1 g[1] = [] // Bomb 1 cannot trigger any bombs g[2] = [] // Bomb 2 cannot trigger any bombs
Step 2: BFS from Each Starting Bomb
Starting from Bomb 0:
- Initialize:
vis = {0}
,q = [0]
- Process Bomb 0: Check neighbors in
g[0] = [1]
- Bomb 1 not visited, add to
vis = {0, 1}
andq = [1]
- Bomb 1 not visited, add to
- Process Bomb 1: Check neighbors in
g[1] = []
(no neighbors) - Queue empty, BFS complete
- Total detonations: 2 (Bombs 0 and 1)
Starting from Bomb 1:
- Initialize:
vis = {1}
,q = [1]
- Process Bomb 1: Check neighbors in
g[1] = []
(no neighbors) - Queue empty, BFS complete
- Total detonations: 1 (only Bomb 1)
Starting from Bomb 2:
- Initialize:
vis = {2}
,q = [2]
- Process Bomb 2: Check neighbors in
g[2] = []
(no neighbors) - Queue empty, BFS complete
- Total detonations: 1 (only Bomb 2)
Step 3: Find Maximum
Maximum detonations = max(2, 1, 1) = 2
Therefore, by choosing to detonate Bomb 0 initially, we can achieve the maximum of 2 total detonations through the chain reaction.
Solution Implementation
1class Solution:
2 def maximumDetonation(self, bombs: List[List[int]]) -> int:
3 # Get the total number of bombs
4 num_bombs = len(bombs)
5
6 # Build adjacency list: graph[i] contains bombs that bomb i can detonate
7 graph = [[] for _ in range(num_bombs)]
8
9 # Check each pair of bombs to see if one can detonate the other
10 for i in range(num_bombs - 1):
11 x1, y1, radius1 = bombs[i]
12 for j in range(i + 1, num_bombs):
13 x2, y2, radius2 = bombs[j]
14
15 # Calculate Euclidean distance between bombs
16 distance = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
17
18 # If bomb i can reach bomb j with its blast radius
19 if distance <= radius1:
20 graph[i].append(j)
21
22 # If bomb j can reach bomb i with its blast radius
23 if distance <= radius2:
24 graph[j].append(i)
25
26 # Try detonating each bomb as the starting point
27 max_detonations = 0
28
29 for start_bomb in range(num_bombs):
30 # Track which bombs have been detonated
31 visited = {start_bomb}
32
33 # BFS queue to process chain reactions
34 queue = [start_bomb]
35
36 # Process all bombs that can be detonated in chain reaction
37 for current_bomb in queue:
38 for neighbor_bomb in graph[current_bomb]:
39 if neighbor_bomb not in visited:
40 visited.add(neighbor_bomb)
41 queue.append(neighbor_bomb)
42
43 # Early termination: if all bombs detonated, return immediately
44 if len(visited) == num_bombs:
45 return num_bombs
46
47 # Update maximum number of detonations
48 max_detonations = max(max_detonations, len(visited))
49
50 return max_detonations
51
1class Solution {
2 public int maximumDetonation(int[][] bombs) {
3 int n = bombs.length;
4
5 // Build adjacency list graph where edge i->j means bomb i can detonate bomb j
6 List<Integer>[] graph = new List[n];
7 Arrays.setAll(graph, index -> new ArrayList<>());
8
9 // Check all pairs of bombs to determine detonation relationships
10 for (int i = 0; i < n - 1; i++) {
11 for (int j = i + 1; j < n; j++) {
12 int[] bomb1 = bombs[i];
13 int[] bomb2 = bombs[j];
14
15 // Calculate Euclidean distance between bomb centers
16 double distance = Math.hypot(bomb1[0] - bomb2[0], bomb1[1] - bomb2[1]);
17
18 // If bomb1's blast radius reaches bomb2, add directed edge i->j
19 if (distance <= bomb1[2]) {
20 graph[i].add(j);
21 }
22
23 // If bomb2's blast radius reaches bomb1, add directed edge j->i
24 if (distance <= bomb2[2]) {
25 graph[j].add(i);
26 }
27 }
28 }
29
30 int maxDetonations = 0;
31 boolean[] visited = new boolean[n];
32
33 // Try detonating each bomb as the starting point
34 for (int startBomb = 0; startBomb < n; startBomb++) {
35 // Reset visited array for each starting bomb
36 Arrays.fill(visited, false);
37 visited[startBomb] = true;
38
39 // Count bombs detonated using BFS from current starting bomb
40 int detonationCount = 0;
41 Deque<Integer> queue = new ArrayDeque<>();
42 queue.offer(startBomb);
43
44 // BFS to find all reachable bombs from startBomb
45 while (!queue.isEmpty()) {
46 int currentBomb = queue.poll();
47 detonationCount++;
48
49 // Check all bombs that can be detonated by currentBomb
50 for (int nextBomb : graph[currentBomb]) {
51 if (!visited[nextBomb]) {
52 visited[nextBomb] = true;
53 queue.offer(nextBomb);
54 }
55 }
56 }
57
58 // Early exit if all bombs can be detonated
59 if (detonationCount == n) {
60 return n;
61 }
62
63 // Update maximum detonations found
64 maxDetonations = Math.max(maxDetonations, detonationCount);
65 }
66
67 return maxDetonations;
68 }
69}
70
1class Solution {
2public:
3 int maximumDetonation(vector<vector<int>>& bombs) {
4 int n = bombs.size();
5
6 // Build adjacency list graph where edge i->j means bomb i can detonate bomb j
7 vector<vector<int>> graph(n);
8
9 // Check all pairs of bombs to build the detonation graph
10 for (int i = 0; i < n - 1; ++i) {
11 for (int j = i + 1; j < n; ++j) {
12 // Get references to current pair of bombs
13 vector<int>& bomb1 = bombs[i];
14 vector<int>& bomb2 = bombs[j];
15
16 // Calculate Euclidean distance between bomb centers
17 double distance = hypot(bomb1[0] - bomb2[0], bomb1[1] - bomb2[1]);
18
19 // If bomb1's explosion radius reaches bomb2, add directed edge
20 if (distance <= bomb1[2]) {
21 graph[i].push_back(j);
22 }
23
24 // If bomb2's explosion radius reaches bomb1, add directed edge
25 if (distance <= bomb2[2]) {
26 graph[j].push_back(i);
27 }
28 }
29 }
30
31 // Find maximum number of bombs that can be detonated
32 int maxDetonated = 0;
33
34 // Try starting from each bomb
35 for (int startBomb = 0; startBomb < n; ++startBomb) {
36 // Track visited bombs for current BFS
37 vector<bool> visited(n, false);
38
39 // BFS to count reachable bombs from current starting bomb
40 queue<int> bfsQueue;
41 bfsQueue.push(startBomb);
42 visited[startBomb] = true;
43 int detonatedCount = 0;
44
45 while (!bfsQueue.empty()) {
46 int currentBomb = bfsQueue.front();
47 bfsQueue.pop();
48 ++detonatedCount;
49
50 // Explore all bombs that can be detonated by current bomb
51 for (int nextBomb : graph[currentBomb]) {
52 if (!visited[nextBomb]) {
53 visited[nextBomb] = true;
54 bfsQueue.push(nextBomb);
55 }
56 }
57 }
58
59 // Early termination if all bombs can be detonated
60 if (detonatedCount == n) {
61 return n;
62 }
63
64 maxDetonated = max(maxDetonated, detonatedCount);
65 }
66
67 return maxDetonated;
68 }
69};
70
1/**
2 * Calculates the maximum number of bombs that can be detonated by choosing one bomb to detonate.
3 * When a bomb detonates, it will detonate all other bombs within its blast radius.
4 *
5 * @param bombs - Array of bombs where each bomb is [x, y, radius]
6 * @returns Maximum number of bombs that can be detonated
7 */
8function maximumDetonation(bombs: number[][]): number {
9 const bombCount: number = bombs.length;
10
11 // Build adjacency list graph where edge from i to j means bomb i can detonate bomb j
12 const adjacencyGraph: number[][] = Array.from({ length: bombCount }, () => []);
13
14 // Check all pairs of bombs to determine which bombs can detonate each other
15 for (let i = 0; i < bombCount - 1; i++) {
16 const [x1, y1, radius1] = bombs[i];
17
18 for (let j = i + 1; j < bombCount; j++) {
19 const [x2, y2, radius2] = bombs[j];
20
21 // Calculate Euclidean distance between bomb centers
22 const distance: number = Math.hypot(x1 - x2, y1 - y2);
23
24 // If bomb i's blast radius reaches bomb j, add edge from i to j
25 if (distance <= radius1) {
26 adjacencyGraph[i].push(j);
27 }
28
29 // If bomb j's blast radius reaches bomb i, add edge from j to i
30 if (distance <= radius2) {
31 adjacencyGraph[j].push(i);
32 }
33 }
34 }
35
36 let maxDetonations: number = 0;
37
38 // Try detonating each bomb as the starting point
39 for (let startBomb = 0; startBomb < bombCount; startBomb++) {
40 // Track visited bombs in current chain reaction
41 const visitedBombs: Set<number> = new Set([startBomb]);
42
43 // BFS queue to process bombs that will detonate
44 const detonationQueue: number[] = [startBomb];
45
46 // Process all bombs that will detonate in chain reaction
47 for (const currentBomb of detonationQueue) {
48 // Check all bombs that can be detonated by current bomb
49 for (const nextBomb of adjacencyGraph[currentBomb]) {
50 if (!visitedBombs.has(nextBomb)) {
51 visitedBombs.add(nextBomb);
52 detonationQueue.push(nextBomb);
53 }
54 }
55 }
56
57 // Early exit if all bombs can be detonated
58 if (visitedBombs.size === bombCount) {
59 return bombCount;
60 }
61
62 // Update maximum detonations found
63 maxDetonations = Math.max(maxDetonations, visitedBombs.size);
64 }
65
66 return maxDetonations;
67}
68
Time and Space Complexity
Time Complexity: O(nΒ³)
The time complexity analysis breaks down as follows:
- Building the graph requires
O(nΒ²)
time, as we have a nested loop comparing each pair of bombs once (lines 5-12) - The BFS/graph traversal section (lines 13-22) runs
n
times (once for each starting bomb) - For each starting position, we perform a BFS that in the worst case visits all
n
bombs - Each bomb can have up to
n-1
edges in its adjacency list - Therefore, the worst-case time for all BFS traversals is
O(n) Γ O(nΒ²) = O(nΒ³)
- Outer loop:
n
iterations - BFS can visit up to
n
nodes - Each node can have up to
n-1
neighbors to check
- Outer loop:
Space Complexity: O(nΒ²)
The space complexity analysis:
- The adjacency list
g
can store up toO(nΒ²)
edges in the worst case (when every bomb can detonate every other bomb) - The visited set
vis
usesO(n)
space - The queue
q
usesO(n)
space in the worst case - Since
O(nΒ²) + O(n) + O(n) = O(nΒ²)
, the overall space complexity isO(nΒ²)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow in Distance Calculation
One of the most common pitfalls in this problem is using integer arithmetic for distance comparison, which can cause integer overflow for large coordinates.
Problematic approach:
# This can overflow for large values distance_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2 if distance_squared <= radius ** 2: # Comparing squared values # Add edge
Why it's problematic:
- When coordinates are near the constraint limits (e.g.,
10^5
), the squared differences can exceed integer limits - Python handles big integers well, but in other languages this causes overflow
- Even in Python, comparing very large integers can be less efficient
Better solution:
# Use floating-point distance calculation distance = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 if distance <= radius: # Add edge
2. Bidirectional Edge Confusion
Another pitfall is incorrectly assuming the detonation relationship is symmetric. Just because bomb A can detonate bomb B doesn't mean bomb B can detonate bomb A.
Incorrect approach:
for i in range(num_bombs):
for j in range(i + 1, num_bombs):
distance = calculate_distance(bombs[i], bombs[j])
if distance <= bombs[i][2]: # Only checking one radius
graph[i].append(j)
graph[j].append(i) # Wrong! Assuming symmetry
Correct approach:
for i in range(num_bombs):
for j in range(i + 1, num_bombs):
distance = calculate_distance(bombs[i], bombs[j])
if distance <= bombs[i][2]: # Bomb i can detonate bomb j
graph[i].append(j)
if distance <= bombs[j][2]: # Bomb j can detonate bomb i
graph[j].append(i)
3. Using DFS Instead of BFS
While both DFS and BFS work correctly for this problem, using recursive DFS can lead to stack overflow for large inputs with deep chains.
Risky recursive DFS:
def dfs(bomb, visited, graph):
visited.add(bomb)
for neighbor in graph[bomb]:
if neighbor not in visited:
dfs(neighbor, visited, graph) # Can cause stack overflow
Safer iterative BFS (as shown in solution):
queue = [start_bomb] for current_bomb in queue: for neighbor_bomb in graph[current_bomb]: if neighbor_bomb not in visited: visited.add(neighbor_bomb) queue.append(neighbor_bomb)
The iterative BFS approach avoids recursion depth issues and is generally more memory-efficient for this type of graph traversal problem.
Which data structure is used to implement priority queue?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
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
Want a Structured Path to Master System Design Too? Donβt Miss This!