Facebook Pixel

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:

  1. Equal distribution: Every child must receive exactly the same number of candies
  2. 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.)
  3. No merging: You cannot combine different piles together
  4. Single pile per child: Each child can only receive candies from one pile (either an original pile or a sub-pile you created)
  5. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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) and r = 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:

  1. While l < r, we continue searching for the optimal value

  2. 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 and r are adjacent
  3. Feasibility Check: For the current mid value, calculate how many children can receive mid candies:

    • Use sum(x // mid for x in candies) to count total portions
    • For each pile x, we can create x // mid portions of size mid
    • Sum all these portions to get the total number of children we can serve
  4. Search Space Adjustment:

    • If sum(x // mid for x in candies) >= k:
      • We can serve at least k children with mid candies each
      • This value is feasible, so update l = mid (search for potentially larger values)
    • Otherwise:
      • We cannot serve k children with mid candies each
      • This value is too large, so update r = mid - 1 (search for smaller values)
  5. 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 exactly k children
  • Any value larger than l would result in fewer than k 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 Evaluator

Example 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 ✓
  • 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 ✗
  • 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 ✓
  • 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 takes O(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 gives mid = 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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these properties could exist for a graph but not a tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More