1552. Magnetic Force Between Two Balls
Problem Description
You have n baskets at different positions along a line, where the i-th basket is located at position[i]. You need to place m balls into these baskets.
When two balls are placed in baskets, they exert a magnetic force on each other. The magnetic force between two balls at positions x and y is defined as the absolute distance between them: |x - y|.
Your goal is to distribute the m balls into the baskets in such a way that the minimum magnetic force between any two balls is maximized. In other words, you want to place the balls so that the closest pair of balls is as far apart as possible.
Given:
- An integer array
positionrepresenting the positions of thenbaskets - An integer
mrepresenting the number of balls to place
Return the maximum possible value of the minimum magnetic force between any two balls.
For example, if you have baskets at positions [1, 2, 3, 4, 7] and need to place 3 balls, one possible arrangement could be placing balls at positions 1, 4, and 7. This would give magnetic forces of |1-4| = 3, |1-7| = 6, and |4-7| = 3. The minimum magnetic force in this arrangement is 3.
Intuition
The key insight is recognizing that this problem has a monotonic property: as we increase the minimum required distance between balls, the number of balls we can successfully place decreases. If we can place m balls with a minimum distance of d, then we can definitely place m balls with any minimum distance less than d (we just have more flexibility in placement).
This monotonic relationship suggests using binary search on the answer. Instead of directly trying to find the optimal placement, we can flip the problem: given a specific minimum distance f, can we place all m balls such that every pair is at least f apart?
To check if a given minimum distance f is achievable, we can use a greedy approach: sort the basket positions and place balls from left to right, always placing the next ball in the leftmost available basket that is at least distance f from the previous ball. This greedy strategy works because placing a ball as far left as possible gives us the most room for placing subsequent balls.
The search space for our answer is bounded. The minimum possible distance is 1 (when balls are in adjacent positions), and the maximum possible distance cannot exceed the total span of the baskets (position[-1] - position[0] after sorting). We binary search within this range to find the largest minimum distance that still allows us to place all m balls.
The algorithm transforms the optimization problem ("maximize the minimum distance") into a decision problem ("can we achieve at least this minimum distance?"), which is easier to solve and perfect for binary search.
Learn more about Binary Search and Sorting patterns.
Solution Implementation
1from typing import List
2
3class Solution:
4 def maxDistance(self, position: List[int], m: int) -> int:
5 """
6 Find the maximum minimum distance between m balls placed in given positions.
7 Uses binary search template to find the first distance where placement fails.
8 """
9
10 def can_place_balls(min_distance: int) -> bool:
11 """
12 Check if we can place m balls with at least min_distance apart.
13 Returns True if placement is possible, False otherwise.
14 """
15 balls_placed = 1 # Place first ball at position[0]
16 previous_position = position[0]
17
18 for i in range(1, len(position)):
19 if position[i] - previous_position >= min_distance:
20 balls_placed += 1
21 previous_position = position[i]
22 if balls_placed == m:
23 return True
24
25 return False
26
27 # Sort positions to enable greedy placement
28 position.sort()
29
30 # Binary search using the standard template
31 # Feasible function: NOT can_place_balls(d) - true when we cannot place m balls
32 left, right = 1, position[-1] - position[0]
33 first_true_index = -1
34
35 while left <= right:
36 mid = (left + right) // 2
37
38 if not can_place_balls(mid):
39 # Feasible: cannot place m balls with this distance
40 first_true_index = mid
41 right = mid - 1
42 else:
43 # Not feasible: can still place m balls, try larger distance
44 left = mid + 1
45
46 # The answer is the largest distance where we CAN place all balls
47 if first_true_index == -1:
48 # All distances work, return the maximum
49 return position[-1] - position[0]
50 else:
51 # Return the distance just before placement becomes impossible
52 return first_true_index - 1
531class Solution {
2 private int[] positions;
3
4 /**
5 * Find the maximum minimum distance between any two balls in m baskets.
6 * Uses binary search template to find first distance where placement fails.
7 */
8 public int maxDistance(int[] position, int m) {
9 // Sort positions to enable greedy placement
10 Arrays.sort(position);
11 this.positions = position;
12
13 // Binary search using the standard template
14 // Feasible: cannot place m balls with this distance
15 int left = 1;
16 int right = position[position.length - 1] - position[0];
17 int firstTrueIndex = -1;
18
19 while (left <= right) {
20 int mid = left + (right - left) / 2;
21
22 if (!canPlaceBalls(mid, m)) {
23 // Feasible: cannot place m balls with this distance
24 firstTrueIndex = mid;
25 right = mid - 1;
26 } else {
27 // Not feasible: can still place m balls, try larger distance
28 left = mid + 1;
29 }
30 }
31
32 // Return the largest distance where we CAN place all balls
33 if (firstTrueIndex == -1) {
34 return position[position.length - 1] - position[0];
35 }
36 return firstTrueIndex - 1;
37 }
38
39 /**
40 * Check if we can place m balls with at least minDistance apart.
41 * Returns true if placement is possible.
42 */
43 private boolean canPlaceBalls(int minDistance, int m) {
44 int ballCount = 1; // Place first ball at positions[0]
45 int previousPosition = positions[0];
46
47 for (int i = 1; i < positions.length; i++) {
48 if (positions[i] - previousPosition >= minDistance) {
49 ballCount++;
50 previousPosition = positions[i];
51 if (ballCount == m) {
52 return true;
53 }
54 }
55 }
56
57 return false;
58 }
59}
601class Solution {
2public:
3 int maxDistance(vector<int>& position, int m) {
4 // Sort positions to enable greedy placement
5 sort(position.begin(), position.end());
6
7 // Lambda to check if we can place m balls with minDist apart
8 auto canPlaceBalls = [&](int minDist) -> bool {
9 int ballCount = 1; // Place first ball at position[0]
10 int previousPosition = position[0];
11
12 for (int i = 1; i < position.size(); i++) {
13 if (position[i] - previousPosition >= minDist) {
14 ballCount++;
15 previousPosition = position[i];
16 if (ballCount == m) {
17 return true;
18 }
19 }
20 }
21 return false;
22 };
23
24 // Binary search using the standard template
25 // Feasible: cannot place m balls with this distance
26 int left = 1;
27 int right = position.back() - position.front();
28 int firstTrueIndex = -1;
29
30 while (left <= right) {
31 int mid = left + (right - left) / 2;
32
33 if (!canPlaceBalls(mid)) {
34 // Feasible: cannot place m balls with this distance
35 firstTrueIndex = mid;
36 right = mid - 1;
37 } else {
38 // Not feasible: can still place m balls, try larger distance
39 left = mid + 1;
40 }
41 }
42
43 // Return the largest distance where we CAN place all balls
44 if (firstTrueIndex == -1) {
45 return position.back() - position.front();
46 }
47 return firstTrueIndex - 1;
48 }
49};
501/**
2 * Finds the maximum minimum distance between any two balls placed in positions.
3 * Uses binary search template to find first distance where placement fails.
4 */
5function maxDistance(position: number[], m: number): number {
6 // Sort positions to enable greedy placement
7 position.sort((a, b) => a - b);
8
9 /**
10 * Check if we can place m balls with at least minDistance apart.
11 * Returns true if placement is possible.
12 */
13 const canPlaceBalls = (minDistance: number): boolean => {
14 let ballCount = 1; // Place first ball at position[0]
15 let previousPosition = position[0];
16
17 for (let i = 1; i < position.length; i++) {
18 if (position[i] - previousPosition >= minDistance) {
19 ballCount++;
20 previousPosition = position[i];
21 if (ballCount === m) {
22 return true;
23 }
24 }
25 }
26
27 return false;
28 };
29
30 // Binary search using the standard template
31 // Feasible: cannot place m balls with this distance
32 let left = 1;
33 let right = position[position.length - 1] - position[0];
34 let firstTrueIndex = -1;
35
36 while (left <= right) {
37 const mid = Math.floor((left + right) / 2);
38
39 if (!canPlaceBalls(mid)) {
40 // Feasible: cannot place m balls with this distance
41 firstTrueIndex = mid;
42 right = mid - 1;
43 } else {
44 // Not feasible: can still place m balls, try larger distance
45 left = mid + 1;
46 }
47 }
48
49 // Return the largest distance where we CAN place all balls
50 if (firstTrueIndex === -1) {
51 return position[position.length - 1] - position[0];
52 }
53 return firstTrueIndex - 1;
54}
55Solution Approach
The solution implements binary search using the standard template combined with a greedy verification function.
1. Sort the positions array: First, we sort the basket positions to enable the greedy placement strategy.
2. Define the feasible function: We define feasible(d) as "we CANNOT place m balls with minimum distance d". This creates a pattern:
[false, false, ..., false, true, true, ..., true]
We want to find the first distance where placement becomes impossible.
3. Greedy verification: To check if we can place m balls with distance d:
- Place the first ball at position[0]
- Greedily place each subsequent ball at the first position that is at least
daway from the previous ball - Count how many balls we can place
- Return
count < m(true if we cannot place all m balls)
4. Binary search using the template:
left, right = 1, position[-1] - position[0] first_true_index = -1 while left <= right: mid = (left + right) // 2 if not can_place_balls(mid): # feasible: cannot place m balls first_true_index = mid right = mid - 1 else: left = mid + 1
5. Calculate the answer: The answer is first_true_index - 1 (the largest distance where we CAN place all balls). If first_true_index == -1, all distances in range work, so return the maximum distance.
Time Complexity: O(n log n + n log M) where n is the number of baskets and M is the position range.
Space Complexity: O(log n) for sorting.
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 with baskets at positions [1, 2, 3, 4, 7] and we need to place m = 3 balls.
Step 1: Sort positions
The array is already sorted: [1, 2, 3, 4, 7]
Step 2: Understand the feasible function
feasible(d) = "cannot place 3 balls with minimum distance d"
- d=1: can place at 1,2,3 → false (can place)
- d=2: can place at 1,3,7 → false (can place)
- d=3: can place at 1,4,7 → false (can place)
- d=4: can only place at 1,7 (2 balls) → true (cannot place 3)
- d=5: can only place at 1,7 (2 balls) → true (cannot place 3)
- d=6: can only place at 1,7 (2 balls) → true (cannot place 3)
Pattern: [false, false, false, true, true, true] → first_true_index = 4
Step 3: Binary search using template
Initialize: left = 1, right = 6, first_true_index = -1
Iteration 1: left=1, right=6
mid = (1 + 6) // 2 = 3- Can place 3 balls with distance 3? Place at 1, then 4, then 7 → Yes!
can_place_balls(3) = true, so feasible(3) = falseleft = mid + 1 = 4
Iteration 2: left=4, right=6
mid = (4 + 6) // 2 = 5- Can place 3 balls with distance 5? Place at 1, then need 6+, only 7 works → Only 2 balls
can_place_balls(5) = false, so feasible(5) = truefirst_true_index = 5,right = mid - 1 = 4
Iteration 3: left=4, right=4
mid = (4 + 4) // 2 = 4- Can place 3 balls with distance 4? Place at 1, then need 5+, only 7 works → Only 2 balls
can_place_balls(4) = false, so feasible(4) = truefirst_true_index = 4,right = mid - 1 = 3
Loop ends as left > right (4 > 3).
Step 4: Calculate the answer
first_true_index = 4(first distance where we cannot place 3 balls)- Answer =
first_true_index - 1 = 3(last distance where we can place 3 balls)
Final Answer: 3
The optimal placement is at positions 1, 4, and 7, giving minimum distance 3.
Time and Space Complexity
Time Complexity: O(n × log n + n × log M)
The time complexity consists of two main parts:
O(n × log n)for sorting thepositionarray, wherenis the length of the position arrayO(n × log M)for the binary search operation, where:- The binary search runs
O(log M)iterations, withM = position[-1] - 1being the search range (from 1 to the maximum position value) - Each iteration calls the
checkfunction, which iterates through allnpositions inO(n)time - Combined:
O(n × log M)
- The binary search runs
Space Complexity: O(log n)
The space complexity comes from:
- The sorting algorithm (typically Timsort in Python) uses
O(log n)space for its recursion stack - The
checkfunction usesO(1)additional space with only a few variables (prev,cnt,curr) - The binary search itself uses
O(1)space for variableslandr - The dominant factor is the sorting's recursion stack:
O(log n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common mistake is using while left < right with left = mid (finding last true) instead of the standard template with first_true_index.
Wrong variant:
while left < right: mid = (left + right + 1) // 2 # Ceiling to avoid infinite loop if can_place_balls(mid): left = mid else: right = mid - 1 return left
Correct template:
first_true_index = -1 while left <= right: mid = (left + right) // 2 if not can_place_balls(mid): # Invert: true when cannot place first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 if first_true_index != -1 else right
2. Incorrect Binary Search Bounds
Setting right bound as position[-1] (a position) instead of position[-1] - position[0] (maximum distance).
Wrong:
right = position[-1] # This is a position, not a distance
Correct:
right = position[-1] - position[0] # Maximum possible distance
3. Off-by-One Error in Result Calculation
When using first_true_index (first distance where placement fails), the answer is first_true_index - 1.
Wrong:
return first_true_index # Returns first invalid distance!
Correct:
return first_true_index - 1 # Last valid distance
4. Not Handling first_true_index == -1
If all distances allow placement, first_true_index stays -1. Must handle this case.
Solution:
if first_true_index == -1: return position[-1] - position[0] # All distances work return first_true_index - 1
5. Greedy Placement Starting Position
Always place the first ball at position[0]. Don't use -inf which complicates logic.
Simpler approach:
def can_place_balls(min_distance):
balls_placed = 1 # First ball at position[0]
prev = position[0]
for i in range(1, len(position)):
if position[i] - prev >= min_distance:
balls_placed += 1
prev = position[i]
return balls_placed >= m
Template-Compliant Solution:
def maxDistance(self, position: List[int], m: int) -> int:
def can_place_balls(min_distance: int) -> bool:
balls_placed = 1
prev = position[0]
for i in range(1, len(position)):
if position[i] - prev >= min_distance:
balls_placed += 1
prev = position[i]
return balls_placed >= m
position.sort()
left, right = 1, position[-1] - position[0]
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if not can_place_balls(mid):
first_true_index = mid
right = mid - 1
else:
left = mid + 1
if first_true_index == -1:
return position[-1] - position[0]
return first_true_index - 1
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!