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 key observation is that this problem has a monotonic property: if we can give each child v
candies, then we can definitely give each child any amount less than v
candies. This is because we can always take smaller portions from the piles we divided.
Think about it this way - if we can successfully divide our candy piles to give 5 candies to each of k
children, we can certainly give them 4, 3, 2, or 1 candy each instead. We'd just be taking less from each pile.
This monotonic property immediately suggests binary search. We're looking for the maximum value that satisfies our condition, and we know that:
- All values from 0 to this maximum are valid
- All values above this maximum are invalid
To check if a particular value v
is achievable, we need to determine: "Can we create at least k
portions of size v
from our candy piles?"
For each pile of size candies[i]
, the maximum number of portions of size v
we can create from it is candies[i] // v
(integer division). For example, from a pile of 10 candies, we can create 3 portions of size 3 each (with 1 candy left unused).
Therefore, the total number of children we can serve with v
candies each is sum(candies[i] // v for all i)
. If this sum is at least k
, then v
is achievable.
The search space is bounded:
- Lower bound: 0 (worst case, we can't give any candy to each child)
- Upper bound:
max(candies)
(best case, we give the entire largest pile to one child)
By using binary search on this range and checking the feasibility of each middle value, we can efficiently find the maximum number of candies each child can receive.
Learn more about Binary Search patterns.
Solution Approach
The implementation uses binary search to find the maximum number of candies each child can receive.
Binary Search Setup:
- Initialize the search boundaries:
l = 0
(minimum possible candies per child) andr = max(candies)
(maximum possible candies per child) - We use the maximum value in the candies array as the upper bound because that's the theoretical maximum any single child could receive
Binary Search Process:
-
While
l < r
, we continue searching for the optimal value -
Calculate the middle value using
mid = (l + r + 1) >> 1
- The expression
(l + r + 1) >> 1
is equivalent to(l + r + 1) // 2
- We add 1 before dividing to ensure we round up, which prevents infinite loops when
l
andr
are adjacent
- The expression
-
Feasibility Check: For the current
mid
value, calculate how many children can receivemid
candies:- Use
sum(x // mid for x in candies)
to count total portions - For each pile
x
, we can createx // mid
portions of sizemid
- Sum all these portions to get the total number of children we can serve
- Use
-
Search Space Adjustment:
- If
sum(x // mid for x in candies) >= k
:- We can serve at least
k
children withmid
candies each - This value is feasible, so update
l = mid
(search for potentially larger values)
- We can serve at least
- Otherwise:
- We cannot serve
k
children withmid
candies each - This value is too large, so update
r = mid - 1
(search for smaller values)
- We cannot serve
- If
-
Termination: When
l == r
, we've found the maximum feasible value
Why this works:
- The binary search converges to the exact boundary between feasible and infeasible values
- The final value
l
represents the maximum number of candies where we can still serve exactlyk
children - Any value larger than
l
would result in fewer thank
portions being possible
Time Complexity: O(n * log(max(candies)))
where n
is the length of the candies array
- Binary search runs in
O(log(max(candies)))
iterations - Each iteration requires
O(n)
time to check feasibility
Space Complexity: O(1)
- only using a constant amount of extra space
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 the solution with candies = [5, 8, 6]
and k = 3
children.
Step 1: Initialize Binary Search
l = 0
(minimum candies per child)r = max([5, 8, 6]) = 8
(maximum candies per child)
Step 2: First Iteration
mid = (0 + 8 + 1) >> 1 = 4
- Check if we can give 4 candies to each of 3 children:
- From pile of 5:
5 // 4 = 1
portion - From pile of 8:
8 // 4 = 2
portions - From pile of 6:
6 // 4 = 1
portion - Total portions:
1 + 2 + 1 = 4
≥ 3 ✓
- From pile of 5:
- Since 4 portions ≥ 3 children, we can achieve this
- Update:
l = 4
, search range becomes[4, 8]
Step 3: Second Iteration
mid = (4 + 8 + 1) >> 1 = 6
- Check if we can give 6 candies to each of 3 children:
- From pile of 5:
5 // 6 = 0
portions - From pile of 8:
8 // 6 = 1
portion - From pile of 6:
6 // 6 = 1
portion - Total portions:
0 + 1 + 1 = 2
< 3 ✗
- From pile of 5:
- Since 2 portions < 3 children, this is too ambitious
- Update:
r = 6 - 1 = 5
, search range becomes[4, 5]
Step 4: Third Iteration
mid = (4 + 5 + 1) >> 1 = 5
- Check if we can give 5 candies to each of 3 children:
- From pile of 5:
5 // 5 = 1
portion - From pile of 8:
8 // 5 = 1
portion (leaves 3 unused) - From pile of 6:
6 // 5 = 1
portion (leaves 1 unused) - Total portions:
1 + 1 + 1 = 3
≥ 3 ✓
- From pile of 5:
- Since 3 portions = 3 children, we can achieve this
- Update:
l = 5
, search range becomes[5, 5]
Step 5: Termination
l = r = 5
, so we exit the loop- Maximum candies per child = 5
Verification: With answer 5, we can:
- Give child 1: the entire pile of 5 candies
- Give child 2: 5 candies from the pile of 8 (leaving 3 unused)
- Give child 3: 5 candies from the pile of 6 (leaving 1 unused)
Each child receives exactly 5 candies, satisfying all constraints.
Solution Implementation
1class Solution:
2 def maximumCandies(self, candies: List[int], k: int) -> int:
3 # Binary search on the answer: find maximum candies each child can get
4 # Left boundary: minimum possible candies per child (0)
5 # Right boundary: maximum possible candies per child (max pile size)
6 left, right = 0, max(candies)
7
8 # Binary search to find the maximum valid allocation
9 while left < right:
10 # Calculate mid point, using (left + right + 1) // 2 to avoid infinite loop
11 # when left and right are adjacent (ensures mid rounds up)
12 mid = (left + right + 1) // 2
13
14 # Check if we can allocate 'mid' candies to each of k children
15 # Count total number of children we can serve with 'mid' candies each
16 total_children_served = sum(pile_size // mid for pile_size in candies)
17
18 if total_children_served >= k:
19 # If we can serve k or more children with 'mid' candies each,
20 # try to find a larger allocation (move left boundary up)
21 left = mid
22 else:
23 # If we cannot serve k children with 'mid' candies each,
24 # we need to reduce the allocation (move right boundary down)
25 right = mid - 1
26
27 # Return the maximum candies each child can get
28 return left
29
1class Solution {
2 public int maximumCandies(int[] candies, long k) {
3 // Binary search range: minimum 0, maximum is the largest pile size
4 int left = 0;
5 int right = Arrays.stream(candies).max().getAsInt();
6
7 // Binary search to find the maximum candies each child can get
8 while (left < right) {
9 // Calculate mid point, using (left + right + 1) / 2 to avoid infinite loop
10 // when left and right are adjacent
11 int mid = (left + right + 1) >> 1;
12
13 // Count how many children can get 'mid' candies
14 long childrenCount = 0;
15 for (int candyPile : candies) {
16 // Each pile can serve candyPile/mid children
17 childrenCount += candyPile / mid;
18 }
19
20 // Check if we can serve at least k children with mid candies each
21 if (childrenCount >= k) {
22 // Can give mid candies to each child, try to find a larger value
23 left = mid;
24 } else {
25 // Cannot give mid candies to k children, reduce the target
26 right = mid - 1;
27 }
28 }
29
30 // Return the maximum number of candies each child can get
31 return left;
32 }
33}
34
1class Solution {
2public:
3 int maximumCandies(vector<int>& candies, long long k) {
4 // Binary search on the answer: find the maximum candies each child can get
5 // Left boundary: minimum possible candies per child (0)
6 int left = 0;
7 // Right boundary: maximum possible candies per child (max element in array)
8 int right = ranges::max(candies);
9
10 // Binary search to find the maximum valid answer
11 while (left < right) {
12 // Calculate mid point (use (left + right + 1) / 2 to avoid infinite loop)
13 // The +1 ensures we round up, preventing stuck conditions when left = mid
14 int mid = (left + right + 1) >> 1;
15
16 // Count how many children can get 'mid' candies
17 long long childrenCount = 0;
18 for (int candyPile : candies) {
19 // Each pile can be divided into candyPile/mid portions of size mid
20 childrenCount += candyPile / mid;
21 }
22
23 // Check if we can give 'mid' candies to at least k children
24 if (childrenCount >= k) {
25 // We can give mid candies to k children, try for a larger value
26 left = mid;
27 } else {
28 // Cannot give mid candies to k children, try smaller values
29 right = mid - 1;
30 }
31 }
32
33 // Return the maximum candies each child can get
34 return left;
35 }
36};
37
1/**
2 * Finds the maximum number of candies each child can receive when distributing candies to k children
3 * Uses binary search to find the optimal allocation
4 *
5 * @param candies - Array containing the number of candies in each pile
6 * @param k - Number of children to distribute candies to
7 * @returns Maximum number of candies each child can receive
8 */
9function maximumCandies(candies: number[], k: number): number {
10 // Initialize binary search boundaries
11 // left: minimum possible candies per child (0)
12 // right: maximum possible candies per child (largest pile)
13 let left: number = 0;
14 let right: number = Math.max(...candies);
15
16 // Binary search for the maximum valid allocation
17 while (left < right) {
18 // Calculate middle value (using bit shift for integer division)
19 // Adding 1 before shifting ensures we round up to avoid infinite loop
20 const middle: number = (left + right + 1) >> 1;
21
22 // Count how many children can receive 'middle' candies
23 // by dividing each pile by 'middle' and summing the results
24 const childrenCount: number = candies.reduce(
25 (accumulator: number, currentPile: number) => {
26 return accumulator + Math.floor(currentPile / middle);
27 },
28 0
29 );
30
31 // Check if we can satisfy k children with 'middle' candies each
32 if (childrenCount >= k) {
33 // If yes, try to find a larger allocation
34 left = middle;
35 } else {
36 // If no, reduce the allocation amount
37 right = middle - 1;
38 }
39 }
40
41 // Return the maximum candies each child can receive
42 return left;
43}
44
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. Division by Zero Error
The most critical pitfall in this solution is attempting to divide by zero when mid = 0
. This occurs at the beginning of the binary search when checking if we can allocate 0 candies per child.
Problem Code:
mid = (left + right + 1) // 2
total_children_served = sum(pile_size // mid for pile_size in candies) # Error when mid = 0!
Solution:
Add a special check for when mid = 0
:
while left < right:
mid = (left + right + 1) // 2
# Handle the edge case where mid = 0
if mid == 0:
left = 1 # If mid is 0, move to 1 since 0 candies per child is always valid
continue
total_children_served = sum(pile_size // mid for pile_size in candies)
# ... rest of the logic
2. Integer Overflow with Large Values
When dealing with very large candy counts or many children, the sum calculation might overflow in languages with fixed integer sizes (though Python handles arbitrary precision integers).
Solution: For languages with fixed integer sizes, use appropriate data types or add overflow checks:
# Early termination if the answer is clearly 0
if sum(candies) < k:
return 0
3. Incorrect Binary Search Boundaries
Using mid = (left + right) // 2
instead of mid = (left + right + 1) // 2
can cause infinite loops when left
and right
differ by 1.
Problem Scenario:
When left = 5
and right = 6
:
- Using
(left + right) // 2
givesmid = 5
- If the condition passes, we set
left = 5
(no progress!) - The loop never terminates
Solution:
Always use mid = (left + right + 1) // 2
when updating left = mid
in the success case, or restructure the binary search to use left = mid + 1
.
4. Edge Case: More Children than Total Candies
If k
is greater than the total number of candies, the answer should immediately be 0.
Solution: Add an early check:
def maximumCandies(self, candies: List[int], k: int) -> int:
# Early termination for impossible cases
if sum(candies) < k:
return 0
left, right = 0, max(candies)
# ... rest of the binary search
5. Empty Candies Array
If the candies array is empty, max(candies)
will throw an error.
Solution: Add input validation:
def maximumCandies(self, candies: List[int], k: int) -> int:
if not candies or k == 0:
return 0
left, right = 0, max(candies)
# ... rest of the solution
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
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 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
Want a Structured Path to Master System Design Too? Don’t Miss This!