Minimum Number of Operations to Move All Balls to Each Box
MedPrompt
You have n boxes. You are given a binary string boxes of length n, where boxes[i] is '0' if the ith box is empty, and '1' if it contains one ball.
In one operation, you can move one ball from a box to an adjacent box. Box i is adjacent to box j if abs(i - j) == 1. Note that after doing so, there may be more than one ball in some boxes.
Return an array answer of size n, where answer[i] is the minimum number of operations needed to move all the balls to the ith box.
Each answer[i] is calculated considering the initial state of the boxes.
Example 1:
Input: boxes = "110"
Output: [1,1,3]
Explanation: The answer for each box is as follows:
1) First box: you will have to move one ball from the second box to the first box in one operation.
2) Second box: you will have to move one ball from the first box to the second box in one operation.
3) Third box: you will have to move one ball from the first box to the third box in two operations, and move one ball from the second box to the third box in one operation.Example 2:
Input: boxes = "001011"
Output: [11,8,5,4,3,4]
Constraints:
n == boxes.length1 <= n <= 2000boxes[i]is either'0'or'1'.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. For each box, we calculate the cost of moving every ball in the entire set to that specific box. This is done by iterating through all n boxes for each of the n target boxes.
Algorithm
- Initialize an integer array
answerof sizenwith all elements set to 0. - Iterate through each target box
ifrom0ton-1. - For each
i, initialize a variabletotal_ops = 0. - Start a nested loop, iterating through all boxes
jfrom0ton-1. - Inside the nested loop, check if
boxes.charAt(j)is '1'. - If it is, calculate the distance required to move the ball from
jtoi, which isMath.abs(i - j). - Add this distance to
total_ops. - After the inner loop finishes, assign the accumulated
total_opstoanswer[i]. - After the outer loop finishes, return the
answerarray.
Walkthrough
We create an answer array of the same size as the input string boxes. We then use a nested loop structure. The outer loop selects a target box, i. The inner loop scans the entire boxes string to find the locations of all balls. For each ball found at index j, we calculate the number of operations needed to move it to box i. Since one operation moves a ball to an adjacent box, the total operations to move a ball from j to i is simply the absolute difference of their indices, abs(i - j). We sum these costs for all balls for a given target box i and store it in answer[i]. We repeat this process for all possible target boxes.
class Solution { public int[] minOperations(String boxes) { int n = boxes.length(); int[] answer = new int[n]; for (int i = 0; i < n; i++) { int currentOperations = 0; for (int j = 0; j < n; j++) { if (boxes.charAt(j) == '1') { currentOperations += Math.abs(i - j); } } answer[i] = currentOperations; } return answer; }}Complexity
Time
O(n^2), where `n` is the length of the `boxes` string. For each of the `n` boxes, we iterate through all `n` boxes again.
Space
O(n) to store the output array. If the output array is not considered extra space, the complexity is O(1).
Trade-offs
Pros
Very simple and intuitive to understand.
Easy to implement correctly.
Cons
The time complexity is quadratic, which can be slow for larger inputs (though it passes within the given constraints).
A more optimal approach is to use two passes to calculate the costs. The total operations for any box i can be seen as the sum of two parts: the cost to move all balls from the left of i to i, and the cost to move all balls from the right of i to i. We can calculate these two components for all boxes efficiently in two separate linear passes, one from left-to-right and one from right-to-left.
Algorithm
- The total operations for a box
iis the sum of operations to move balls from its left and operations to move balls from its right. - Pass 1 (Left to Right):
- Initialize an
answerarray,ballsCount = 0, andoperations = 0. - Iterate
ifrom0ton-1. - For each
i, allballsCountballs seen so far are now one step further. So, addballsCounttooperations. - Set
answer[i] = operations. - If
boxes.charAt(i)is '1', incrementballsCount.
- Initialize an
- Pass 2 (Right to Left):
- Reset
ballsCount = 0andoperations = 0. - Iterate
ifromn-1down to0. - Similar to the first pass, add
ballsCounttooperations. - Add this
operationsvalue to the existinganswer[i](i.e.,answer[i] += operations). - If
boxes.charAt(i)is '1', incrementballsCount.
- Reset
- Return the
answerarray.
Walkthrough
This approach avoids the redundant calculations of the brute-force method.
In the first pass (left-to-right), we calculate left_ops[i], the cost to move all balls from indices less than i to index i. We can observe that left_ops[i] = left_ops[i-1] + (number of balls to the left of i). We can maintain a running count of balls and operations. We iterate from i = 0 to n-1, updating the operations needed based on the number of balls encountered so far, and store this intermediate result in our answer array.
In the second pass (right-to-left), we do the same for right_ops[i], the cost to move balls from indices greater than i. We iterate from i = n-1 down to 0, again keeping a running count of balls and operations. We add this result to the value already in the answer array.
After both passes, answer[i] will contain the sum of left_ops[i] and right_ops[i], which is the total minimum operations.
class Solution { public int[] minOperations(String boxes) { int n = boxes.length(); int[] answer = new int[n]; int ballsCount = 0; int operations = 0; // Pass 1: Left to Right // Calculate the cost to move balls from the left to the current box i for (int i = 0; i < n; i++) { operations += ballsCount; answer[i] = operations; if (boxes.charAt(i) == '1') { ballsCount++; } } // Pass 2: Right to Left // Calculate the cost to move balls from the right and add it to the previous result ballsCount = 0; operations = 0; for (int i = n - 1; i >= 0; i--) { operations += ballsCount; answer[i] += operations; if (boxes.charAt(i) == '1') { ballsCount++; } } return answer; }}Complexity
Time
O(n), as we perform two independent passes over the array, each taking linear time.
Space
O(n) to store the output array. The extra space used by variables is O(1).
Trade-offs
Pros
Highly efficient with a linear time complexity.
Uses constant extra space (excluding the output array).
Cons
Slightly less intuitive than the brute-force approach, as it requires understanding the relationship between the costs of adjacent boxes.
Solutions
Solution
class Solution {public int[] minOperations(String boxes) { int n = boxes.length(); int[] left = new int[n]; int[] right = new int[n]; for (int i = 1, cnt = 0; i < n; ++i) { if (boxes.charAt(i - 1) == '1') { ++cnt; } left[i] = left[i - 1] + cnt; } for (int i = n - 2, cnt = 0; i >= 0; --i) { if (boxes.charAt(i + 1) == '1') { ++cnt; } right[i] = right[i + 1] + cnt; } int[] ans = new int[n]; for (int i = 0; i < n; ++i) { ans[i] = left[i] + right[i]; } return ans; }}class Solution {public: vector<int> minOperations(string boxes) { int n = boxes.size(); int left[n]; int right[n]; memset(left, 0, sizeof left); memset(right, 0, sizeof right); for (int i = 1, cnt = 0; i < n; ++i) { cnt += boxes[i - 1] == '1'; left[i] = left[i - 1] + cnt; } for (int i = n - 2, cnt = 0; ~i; --i) { cnt += boxes[i + 1] == '1'; right[i] = right[i + 1] + cnt; } vector<int> ans(n); for (int i = 0; i < n; ++i) ans[i] = left[i] + right[i]; return ans; }};class Solution: def minOperations(self, boxes: str) -> List[int]: n = len(boxes) left = [0] * n right = [0] * n cnt = 0 for i in range(1, n): if boxes[i - 1] == '1': cnt += 1 left[i] = left[i - 1] + cnt cnt = 0 for i in range(n - 2, - 1, - 1): if boxes[i + 1] == '1': cnt += 1 right[i] = right[i + 1] + cnt return [a + b for a, b in zip(left, right)]Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.