Loading problems...
TL;DR: The optimal solution uses binary search on the answer to find the smallest maximum bag size that can be achieved within the given operation limit.
In the LeetCode 1760: Minimum Limit of Balls in a Bag problem, you are provided with an array of integers representing bags of balls and an integer maxOperations. You can split a bag into two smaller bags multiple times. The goal is to minimize the size of the largest bag (the "penalty") after performing at most maxOperations splits. This is a classic "minimax" problem where we want to minimize the maximum value in the resulting distribution.
A naive approach might attempt to simulate the splitting process or iterate linearly through possible penalty values.
To brute force the optimal penalty, one could start checking possible answers from 1 up to the maximum value in nums. For a candidate penalty , we calculate if it is possible to reduce all bags to size within . We return the first that satisfies the condition.
maxOperationsPseudo-code:
textmax_val = max(nums) for candidate in 1 to max_val: ops_needed = 0 for bag in nums: ops_needed += (bag - 1) / candidate if ops_needed <= maxOperations: return candidate
Why this fails:
The maximum value in nums can be up to . A linear scan from 1 to results in a time complexity of , where is the array length. With and values up to , this leads to approximately operations, causing a Time Limit Exceeded (TLE) error. We need a logarithmic approach to search the solution space.
The key intuition is to invert the problem. Instead of asking "What is the minimum penalty?", we ask "Given a specific penalty , can we achieve it within maxOperations?"
If we decide that no bag should contain more than balls, we can calculate exactly how many split operations are required for each existing bag.
(S - 1) / K.By summing these required operations for all bags, we get the total cost to achieve penalty . If total_cost <= maxOperations, then is feasible.
Since the feasibility function is monotonic (larger requires fewer operations), we can binary search for the smallest feasible .
Visual Description: Imagine the search space as a horizontal line representing all possible maximum bag sizes, ranging from 1 to the largest initial bag.

Let's trace Example 1: nums = [9], maxOperations = 2.
Initialization:
left = 1, right = 9.Iteration 1:
mid = (1 + 9) / 2 = 5.(9 - 1) / 5 = 1 operation (split 9 -> 4, 5).right = 5.Iteration 2:
mid = (1 + 5) / 2 = 3.(9 - 1) / 3 = 2 operations (split 9 -> 3, 3, 3).right = 3.Iteration 3:
mid = (1 + 3) / 2 = 2.(9 - 1) / 2 = 4 operations.left = 2 + 1 = 3.Termination:
left is 3, right is 3. Loop terminates.left (3).The correctness relies on the monotonicity of the condition function. Let be the minimum operations required to ensure all bags are size .
We are looking for the smallest such that .
Since is non-increasing, the boolean predicate transitions from False to True exactly once as increases. Binary search is guaranteed to find this transition point in logarithmic time relative to the range size.
The formula for operations, (bag - 1) / limit, correctly computes , which is the minimal number of cuts required to partition bag into chunks of size at most limit.
cppclass Solution { public: int minimumSize(vector<int>& nums, int maxOperations) { int left = 1; int right = 0; // The search space upper bound is the largest bag for (int num : nums) { right = max(right, num); } int result = right; while (left <= right) { int mid = left + (right - left) / 2; // Check if it's possible to achieve max bag size 'mid' // within maxOperations long long opsNeeded = 0; for (int num : nums) { if (num > mid) { // Equivalent to ceil(num / mid) - 1 opsNeeded += (num - 1) / mid; } } if (opsNeeded <= maxOperations) { result = mid; right = mid - 1; // Try to achieve a smaller max size } else { left = mid + 1; // Impossible, need larger max size } } return result; } };
javaclass Solution { public int minimumSize(int[] nums, int maxOperations) { int left = 1; int right = 0; // Establish upper bound for (int num : nums) { right = Math.max(right, num); } int result = right; while (left <= right) { int mid = left + (right - left) / 2; if (canAchieve(mid, nums, maxOperations)) { result = mid; right = mid - 1; } else { left = mid + 1; } } return result; } private boolean canAchieve(int maxBagSize, int[] nums, int maxOperations) { long opsNeeded = 0; for (int num : nums) { if (num > maxBagSize) { // Determine splits needed. // (num - 1) / maxBagSize is integer math for ceil(num/maxBagSize) - 1 opsNeeded += (num - 1) / maxBagSize; } // Optimization: Early exit if we exceed limits if (opsNeeded > maxOperations) return false; } return true; } }
pythonclass Solution: def minimumSize(self, nums: List[int], maxOperations: int) -> int: left, right = 1, max(nums) def can_achieve(target_size): ops_needed = 0 for num in nums: if num > target_size: # Calculate required splits # Equivalent to math.ceil(num / target_size) - 1 ops_needed += (num - 1) // target_size return ops_needed <= maxOperations while left < right: mid = (left + right) // 2 if can_achieve(mid): right = mid else: left = mid + 1 return left
Time Complexity:
nums.nums (up to ).Space Complexity:
The "Binary Search on Answer" pattern is a powerful tool for optimization problems involving min-max or max-min criteria. The following problems share the same underlying logic:
Q: Why do I get a Time Limit Exceeded (TLE) error?
numsQ: Why does my solution fail on large inputs even with Binary Search?
int) to accumulate opsNeeded.long long (C++) or long (Java) for the accumulator.Q: Why is my split calculation wrong?
num / mid instead of (num - 1) / mid.6/3 = 2. But you only need 1 split to get [3, 3]. The formula (6-1)/3 = 5/3 = 1 correctly accounts for exact multiples.