2226. Maximum Candies Allocated to K Children
Problem Description
You have an array candies where each element candies[i] represents a pile of candies with that many candies in it. You need to distribute these candies to k children following these rules:
- Equal distribution: Every child must receive exactly the same number of candies
- Pile division: You can split any pile into smaller sub-piles (for example, a pile of 10 candies can be split into piles of 7 and 3, or 5 and 5, etc.)
- No merging: You cannot combine different piles together
- Single pile per child: Each child can only receive candies from one pile (either an original pile or a sub-pile you created)
- Not all piles need to be used: Some piles can remain unallocated
Your goal is to find the maximum number of candies that each child can receive while satisfying all these constraints.
For example, if you have candies = [5, 8, 6] and k = 3 children:
- You could give each child 5 candies by dividing the pile of 8 into 5 and 3, then giving one child the pile of 5, another child the sub-pile of 5, and the third child another pile of 5 from the pile of 6
- You cannot give each child 6 candies because you can only create two piles of size 6 or larger
The solution uses binary search to find the maximum candies per child. The key insight is that if it's possible to give each child v candies, then it's also possible to give each child any amount less than v. The algorithm searches for the highest valid value by checking if we can create at least k piles of size mid from the available candies using the formula sum(x // mid for x in candies) >= k.
Intuition
The problem has a monotonic property: if we can give each child v candies, we can definitely give them any amount less than v. This suggests binary search.
The pattern for this problem is: true, true, ..., true, false, false - we want to find the maximum value where the condition is true (can serve k children).
To use the standard first_true_index template, we invert the condition. Instead of asking "can we serve k children?", we ask "is it impossible to serve k children?":
infeasible(v) = sum(pile // v for pile in candies) < k
This creates the pattern: false, false, ..., false, true, true
- For small values of
v, we can serve k children →infeasible = false - For large values of
v, we cannot serve k children →infeasible = true
The first_true_index gives us the first value where we cannot serve k children. The maximum valid answer is therefore first_true_index - 1.
Edge case: If first_true_index = 1, it means we can't even give 1 candy to each child, so the answer is 0.
The search space is bounded:
left = 1: Minimum meaningful candy amount (0 is always valid but trivial)right = max(candies) + 1: We search up to max+1 to handle the case where max(candies) itself is valid
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def maximumCandies(self, candies: List[int], k: int) -> int:
3 # Infeasible function: returns True when we CANNOT serve k children
4 def infeasible(v: int) -> bool:
5 portions = sum(pile // v for pile in candies)
6 return portions < k
7
8 # Binary search setup
9 left = 1 # Minimum candy amount
10 right = max(candies) + 1 # Extended upper bound
11 first_true_index = 1 # Default: infeasible starts at 1 (answer would be 0)
12
13 # Binary search for the first infeasible value
14 while left <= right:
15 mid = (left + right) // 2
16 if infeasible(mid):
17 first_true_index = mid # Found infeasible value
18 right = mid - 1 # Find smaller infeasible value
19 else:
20 left = mid + 1 # mid is feasible, try larger
21
22 # Maximum feasible = first infeasible - 1
23 return first_true_index - 1
241class Solution {
2 public int maximumCandies(int[] candies, long k) {
3 // Find max pile size for upper bound
4 int maxPile = 0;
5 for (int pile : candies) {
6 maxPile = Math.max(maxPile, pile);
7 }
8
9 // Binary search setup
10 int left = 1;
11 int right = maxPile + 1; // Extended upper bound
12 int firstTrueIndex = 1; // Default: infeasible starts at 1
13
14 // Binary search for the first infeasible value
15 while (left <= right) {
16 int mid = left + (right - left) / 2;
17
18 // Check if infeasible: cannot serve k children with mid candies
19 if (infeasible(candies, mid, k)) {
20 firstTrueIndex = mid;
21 right = mid - 1; // Find smaller infeasible value
22 } else {
23 left = mid + 1; // mid is feasible, try larger
24 }
25 }
26
27 // Maximum feasible = first infeasible - 1
28 return firstTrueIndex - 1;
29 }
30
31 // Returns true when we CANNOT serve k children with v candies each
32 private boolean infeasible(int[] candies, int v, long k) {
33 long portions = 0;
34 for (int pile : candies) {
35 portions += pile / v;
36 }
37 return portions < k;
38 }
39}
401class Solution {
2public:
3 int maximumCandies(vector<int>& candies, long long k) {
4 // Find max pile for upper bound
5 int maxPile = *max_element(candies.begin(), candies.end());
6
7 // Binary search setup
8 int left = 1;
9 int right = maxPile + 1; // Extended upper bound
10 int firstTrueIndex = 1; // Default: infeasible starts at 1
11
12 // Infeasible function: returns true when we CANNOT serve k children
13 auto infeasible = [&](int v) -> bool {
14 long long portions = 0;
15 for (int pile : candies) {
16 portions += pile / v;
17 }
18 return portions < k;
19 };
20
21 // Binary search for the first infeasible value
22 while (left <= right) {
23 int mid = left + (right - left) / 2;
24
25 if (infeasible(mid)) {
26 firstTrueIndex = mid;
27 right = mid - 1; // Find smaller infeasible value
28 } else {
29 left = mid + 1; // mid is feasible, try larger
30 }
31 }
32
33 // Maximum feasible = first infeasible - 1
34 return firstTrueIndex - 1;
35 }
36};
371function maximumCandies(candies: number[], k: number): number {
2 // Infeasible function: returns true when we CANNOT serve k children
3 const infeasible = (v: number): boolean => {
4 let portions = 0;
5 for (const pile of candies) {
6 portions += Math.floor(pile / v);
7 }
8 return portions < k;
9 };
10
11 // Binary search setup
12 const maxPile = Math.max(...candies);
13 let left = 1;
14 let right = maxPile + 1; // Extended upper bound
15 let firstTrueIndex = 1; // Default: infeasible starts at 1
16
17 // Binary search for the first infeasible value
18 while (left <= right) {
19 const mid = Math.floor((left + right) / 2);
20
21 if (infeasible(mid)) {
22 firstTrueIndex = mid;
23 right = mid - 1; // Find smaller infeasible value
24 } else {
25 left = mid + 1; // mid is feasible, try larger
26 }
27 }
28
29 // Maximum feasible = first infeasible - 1
30 return firstTrueIndex - 1;
31}
32Solution Approach
We use the binary search template with an inverted condition to find the maximum candies per child.
Defining the Infeasible Function:
We invert the problem to use the first_true_index template:
infeasible(v) = sum(pile // v for pile in candies) < k
This returns true when we cannot serve k children with v candies each.
Binary Search Setup:
left = 1: Minimum candy amount to checkright = max(candies) + 1: Upper bound (we extend by 1 to handle edge cases)first_true_index = 1: Default to 1 (meaning even 1 candy is infeasible → answer is 0)
Binary Search Template:
left, right = 1, max(candies) + 1
first_true_index = 1 # Default: infeasible starts at 1
while left <= right:
mid = (left + right) // 2
if infeasible(mid): # Cannot serve k children
first_true_index = mid
right = mid - 1 # Find smaller infeasible value
else:
left = mid + 1 # mid is still feasible, try larger
return first_true_index - 1 # Maximum feasible = first infeasible - 1
Why This Works:
- When
infeasible(mid)is true, we found where things break; try smaller values - When
infeasible(mid)is false,midworks; try larger values - The answer is
first_true_index - 1(the last value before things became infeasible)
Time Complexity: O(n × log(max(candies))) where n is the length of the candies array.
Space Complexity: O(1) - only constant extra space used.
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 trace through with candies = [5, 8, 6] and k = 3.
Infeasible function: infeasible(v) = (5//v + 8//v + 6//v) < 3
Initialize:
left = 1,right = max(5, 8, 6) + 1 = 9first_true_index = 1(default)
Iteration 1: left = 1, right = 9
mid = (1 + 9) // 2 = 5- Portions:
5//5 + 8//5 + 6//5 = 1 + 1 + 1 = 3 infeasible(5) = (3 < 3)→ false (we CAN serve 3 children)left = 5 + 1 = 6
Iteration 2: left = 6, right = 9
mid = (6 + 9) // 2 = 7- Portions:
5//7 + 8//7 + 6//7 = 0 + 1 + 0 = 1 infeasible(7) = (1 < 3)→ true (we CANNOT serve 3 children)first_true_index = 7,right = 7 - 1 = 6
Iteration 3: left = 6, right = 6
mid = (6 + 6) // 2 = 6- Portions:
5//6 + 8//6 + 6//6 = 0 + 1 + 1 = 2 infeasible(6) = (2 < 3)→ true (we CANNOT serve 3 children)first_true_index = 6,right = 6 - 1 = 5
Termination: left = 6 > right = 5, exit loop
Result: first_true_index - 1 = 6 - 1 = 5
Verification:
- At v=5: portions = 3 ≥ 3 ✓ (feasible)
- At v=6: portions = 2 < 3 ✗ (infeasible)
- Maximum candies per child = 5
Time and Space Complexity
The time complexity is O(n × log M), where n is the length of the array candies, and M is the maximum value in the array candies. This complexity arises from:
- Binary search performs
O(log M)iterations, as we search in the range[0, max(candies)] - In each iteration, we calculate
sum(x // mid for x in candies), which takesO(n)time to iterate through all elements
The space complexity is O(1), as the algorithm only uses a constant amount of extra space for variables l, r, and mid, regardless of the input size.
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 the standard "find maximum feasible" pattern instead of inverting to first_true_index.
Wrong approach:
while left < right: mid = (left + right + 1) // 2 if feasible(mid): left = mid # Wrong pattern else: right = mid - 1 return left
Correct approach (inverted condition):
first_true_index = 1 # Default while left <= right: mid = (left + right) // 2 if infeasible(mid): # Cannot serve k children first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 # Maximum feasible
2. Division by Zero Error
Starting with left = 0 causes division by zero when mid = 0.
Solution: Always start with left = 1:
left = 1 # Not 0!
right = max(candies) + 1
3. Forgetting to Extend Upper Bound
If max(candies) itself is valid (all piles can contribute 1 portion of that size), we need to check beyond it to find where things become infeasible.
Solution: Use right = max(candies) + 1:
right = max(candies) + 1 # Not just max(candies)
4. Edge Case: More Children than Total Candies
If k > sum(candies), even giving 1 candy each is impossible.
Solution: The template handles this naturally - first_true_index will be 1, so answer is 0.
What data structure does Breadth-first search typically uses to store intermediate states?
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
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
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!