Facebook Pixel

1103. Distribute Candies to People

EasyMathSimulation
Leetcode Link

Problem Description

You have a certain number of candies to distribute among num_people people standing in a row. The distribution follows a specific pattern:

Round 1:

  • Person 1 gets 1 candy
  • Person 2 gets 2 candies
  • Person 3 gets 3 candies
  • ...
  • Person n gets n candies

Round 2:

  • Person 1 gets n + 1 candies
  • Person 2 gets n + 2 candies
  • Person 3 gets n + 3 candies
  • ...
  • Person n gets 2n candies

Subsequent Rounds: The pattern continues with each person receiving one more candy than the previous person received in the entire distribution sequence. After reaching the last person, you go back to the first person and continue.

This process repeats until you run out of candies. When the remaining candies are not enough for the next person's full allocation, that person receives all the remaining candies.

Your task is to return an array of length num_people where each element represents the total number of candies each person receives.

Example walkthrough:

If candies = 10 and num_people = 3:

  • Person 1 gets 1 candy (9 left)
  • Person 2 gets 2 candies (7 left)
  • Person 3 gets 3 candies (4 left)
  • Back to Person 1: gets 4 candies (0 left)

Final distribution: [5, 2, 3]

The solution uses a simulation approach where we iterate through each distribution step, keeping track of which person's turn it is using modulo operation i % num_people, and giving them either the expected amount i + 1 or the remaining candies, whichever is smaller.

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

Intuition

The key insight is recognizing that this is a cyclic distribution problem where we need to keep track of two things: which person's turn it is and how many candies they should receive.

Let's think about the pattern:

  • In the first pass through all people, we give 1, 2, 3, ..., n candies
  • In the second pass, we give n+1, n+2, n+3, ..., 2n candies
  • The pattern continues with each candy distribution being one more than the previous

This suggests we need a counter that continuously increments. If we use a variable i that starts at 0 and increments with each distribution, then i + 1 gives us the number of candies to distribute at each step (1, 2, 3, 4, 5, ...).

To determine which person receives candies, we can use the modulo operator. Since we have num_people people and we cycle through them repeatedly, i % num_people will give us the index of the current person (0, 1, 2, ..., num_people-1, 0, 1, 2, ...).

The edge case to handle is when we don't have enough candies left. At any point, if the remaining candies are less than what the person should receive (i + 1), we simply give them all the remaining candies and stop.

This naturally leads to a simulation approach:

  1. Keep an array to track total candies for each person
  2. Use a counter i to track the distribution sequence
  3. At each step, give person at index i % num_people either i + 1 candies or whatever remains
  4. Continue until all candies are distributed

The beauty of this approach is its simplicity - we're directly modeling the problem statement without needing complex mathematical formulas or optimizations.

Learn more about Math patterns.

Solution Approach

The solution uses a straightforward simulation approach to distribute the candies according to the rules.

Implementation Steps:

  1. Initialize the result array: Create an array ans of size num_people with all elements initialized to 0. This will store the total candies each person receives.

    ans = [0] * num_people
  2. Set up the distribution counter: Initialize i = 0 to track the current distribution round. This counter will continuously increment and help us determine both which person gets candies and how many they should receive.

  3. Main distribution loop: Continue distributing while there are candies remaining:

    while candies:
  4. Determine recipient and amount:

    • The person at index i % num_people is the current recipient
    • They should receive i + 1 candies in this distribution
    • However, if we have fewer candies remaining, they get all that's left: min(candies, i + 1)
    ans[i % num_people] += min(candies, i + 1)
  5. Update remaining candies: Subtract the distributed amount from the total:

    candies -= min(candies, i + 1)
  6. Move to next distribution: Increment the counter for the next round:

    i += 1

Why this works:

  • The modulo operation i % num_people ensures we cycle through people indices 0, 1, 2, ..., num_people-1, then back to 0
  • The distribution amount i + 1 naturally follows the sequence 1, 2, 3, 4, ... as required
  • Using min(candies, i + 1) handles the edge case where we run out of candies mid-distribution
  • The loop automatically terminates when candies becomes 0

Time Complexity: O(√candies) - We distribute approximately 1 + 2 + 3 + ... + k candies, where k(k+1)/2 ≈ candies, so k ≈ √(2*candies)

Space Complexity: O(num_people) - We only use the result array to store the final distribution.

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 a detailed example with candies = 15 and num_people = 4.

We'll track each distribution step to see how the simulation approach works:

Initial Setup:

  • Result array: ans = [0, 0, 0, 0] (one slot per person)
  • Distribution counter: i = 0
  • Remaining candies: 15

Distribution Process:

Step 1: i = 0

  • Person index: 0 % 4 = 0 (Person 1)
  • Candies to give: min(15, 0 + 1) = 1
  • Update: ans[0] += 1ans = [1, 0, 0, 0]
  • Remaining: 15 - 1 = 14
  • Increment: i = 1

Step 2: i = 1

  • Person index: 1 % 4 = 1 (Person 2)
  • Candies to give: min(14, 1 + 1) = 2
  • Update: ans[1] += 2ans = [1, 2, 0, 0]
  • Remaining: 14 - 2 = 12
  • Increment: i = 2

Step 3: i = 2

  • Person index: 2 % 4 = 2 (Person 3)
  • Candies to give: min(12, 2 + 1) = 3
  • Update: ans[2] += 3ans = [1, 2, 3, 0]
  • Remaining: 12 - 3 = 9
  • Increment: i = 3

Step 4: i = 3

  • Person index: 3 % 4 = 3 (Person 4)
  • Candies to give: min(9, 3 + 1) = 4
  • Update: ans[3] += 4ans = [1, 2, 3, 4]
  • Remaining: 9 - 4 = 5
  • Increment: i = 4

Step 5: i = 4 (Back to Person 1!)

  • Person index: 4 % 4 = 0 (Person 1 again)
  • Candies to give: min(5, 4 + 1) = 5
  • Update: ans[0] += 5ans = [6, 2, 3, 4]
  • Remaining: 5 - 5 = 0
  • Loop ends (no candies left)

Final Result: [6, 2, 3, 4]

Key Observations:

  • The modulo operation (i % 4) cycles us through persons 0→1→2→3→0...
  • Each distribution gives i + 1 candies (1, 2, 3, 4, 5, ...)
  • Person 1 received candies twice (1 candy in round 1, 5 candies in round 2)
  • The min() function would handle the case where we run out mid-distribution (though it didn't happen here)

This demonstrates how the simple simulation approach naturally handles the cyclic distribution pattern without complex logic.

Solution Implementation

1class Solution:
2    def distributeCandies(self, candies: int, num_people: int) -> List[int]:
3        # Initialize result array with zeros for each person
4        result = [0] * num_people
5      
6        # Track the current distribution round (also represents candies to give)
7        distribution_round = 0
8      
9        # Continue distributing while there are candies remaining
10        while candies > 0:
11            # Calculate current person's index using modulo
12            current_person = distribution_round % num_people
13          
14            # Give candies: either the required amount (distribution_round + 1) 
15            # or all remaining candies, whichever is smaller
16            candies_to_give = min(candies, distribution_round + 1)
17            result[current_person] += candies_to_give
18          
19            # Reduce the candy count by the amount given
20            candies -= candies_to_give
21          
22            # Move to next distribution round
23            distribution_round += 1
24      
25        return result
26
1class Solution {
2    public int[] distributeCandies(int candies, int num_people) {
3        // Initialize result array to store candies for each person
4        int[] result = new int[num_people];
5      
6        // Distribution loop: i represents the current candy amount to give (1-indexed)
7        for (int i = 0; candies > 0; i++) {
8            // Calculate which person receives candies (using modulo for circular distribution)
9            int personIndex = i % num_people;
10          
11            // Calculate how many candies to give in this round
12            // Either (i + 1) candies or remaining candies, whichever is smaller
13            int candiesToGive = Math.min(candies, i + 1);
14          
15            // Give candies to the current person
16            result[personIndex] += candiesToGive;
17          
18            // Update remaining candies
19            candies -= candiesToGive;
20        }
21      
22        return result;
23    }
24}
25
1class Solution {
2public:
3    vector<int> distributeCandies(int candies, int num_people) {
4        // Initialize result vector to store candies for each person
5        vector<int> result(num_people);
6      
7        // Distribute candies in rounds
8        // i represents the current distribution number (0-indexed)
9        for (int i = 0; candies > 0; ++i) {
10            // Calculate the person index using modulo to cycle through people
11            int personIndex = i % num_people;
12          
13            // Calculate candies to give: either (i+1) or remaining candies, whichever is smaller
14            int candiesToGive = min(candies, i + 1);
15          
16            // Give candies to the current person
17            result[personIndex] += candiesToGive;
18          
19            // Reduce the remaining candies
20            candies -= candiesToGive;
21        }
22      
23        return result;
24    }
25};
26
1/**
2 * Distributes candies to people in a circular manner with increasing amounts
3 * @param candies - Total number of candies to distribute
4 * @param num_people - Number of people to distribute candies to
5 * @returns Array where each element represents candies received by each person
6 */
7function distributeCandies(candies: number, num_people: number): number[] {
8    // Initialize result array with zeros for each person
9    const result: number[] = Array(num_people).fill(0);
10  
11    // Distribute candies in rounds
12    // i represents the current distribution number (0-indexed)
13    for (let distributionNumber = 0; candies > 0; distributionNumber++) {
14        // Calculate current person index using modulo for circular distribution
15        const currentPersonIndex = distributionNumber % num_people;
16      
17        // Calculate candies to give: either remaining candies or (distributionNumber + 1)
18        const candiesToGive = Math.min(candies, distributionNumber + 1);
19      
20        // Give candies to current person
21        result[currentPersonIndex] += candiesToGive;
22      
23        // Reduce remaining candies
24        candies -= candiesToGive;
25    }
26  
27    return result;
28}
29

Time and Space Complexity

The time complexity is O(max(sqrt(candies), num_people)).

To understand why, let's analyze the loop iterations. In each iteration i, we distribute i + 1 candies. The loop continues until all candies are distributed. The total candies distributed follows the pattern: 1 + 2 + 3 + ... + k, where k is the last iteration number.

Using the sum formula for arithmetic sequence: 1 + 2 + 3 + ... + k = k(k+1)/2

When this sum equals approximately candies, we have: k(k+1)/2 ≈ candies, which gives us k ≈ sqrt(2 * candies).

Therefore, the number of iterations is O(sqrt(candies)).

However, we must also consider that the answer array has size num_people, and in edge cases where candies is very small, we might need to ensure at least one pass through all people. Thus, the time complexity is O(max(sqrt(candies), num_people)).

The space complexity is O(num_people) because we create an answer array ans of size num_people to store the distribution results. This is the only additional space used that scales with the input.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow in Mathematical Optimization

A common pitfall occurs when trying to optimize the solution using mathematical formulas to calculate distributions directly. Developers might attempt to find which complete rounds can be distributed using the arithmetic series sum formula, but this can lead to integer overflow issues.

Problematic Approach:

def distributeCandies(self, candies: int, num_people: int) -> List[int]:
    result = [0] * num_people
  
    # Trying to find complete rounds mathematically
    # Sum = n * (n + 1) / 2 where n = complete_rounds * num_people
    # This can cause overflow for large values
    complete_rounds = int((-1 + (1 + 8 * candies) ** 0.5) / 2) // num_people
  
    # ... rest of the implementation

Why it fails:

  • Calculating 8 * candies can overflow for large candy values
  • Square root operations on large numbers can lose precision
  • Integer division might introduce rounding errors

Solution: Stick to the simple simulation approach which avoids these mathematical complexities and handles all cases correctly.

2. Off-by-One Error in Distribution Amount

Developers often confuse whether to use i or i + 1 for the distribution amount.

Incorrect Implementation:

while candies > 0:
    ans[i % num_people] += min(candies, i)  # Wrong: using i instead of i+1
    candies -= min(candies, i)
    i += 1

Why it fails:

  • First person would receive 0 candies in the first round
  • The distribution pattern would be 0, 1, 2, 3... instead of 1, 2, 3, 4...

Solution: Always use i + 1 for the distribution amount since we start counting from 0 but want to give at least 1 candy.

3. Modifying the Loop Counter Inside Complex Logic

Some implementations try to handle special cases by modifying the distribution counter in multiple places.

Problematic Pattern:

i = 0
while candies > 0:
    if candies < i + 1:
        ans[i % num_people] += candies
        break  # Might forget to increment i before breaking
    else:
        ans[i % num_people] += i + 1
        candies -= i + 1
        if some_condition:
            i += 2  # Irregular increment can break the pattern
        else:
            i += 1

Solution: Keep the increment logic simple and consistent - always increment by 1 at the end of each iteration, letting the min() function handle edge cases naturally.

while candies:
    ans[i % num_people] += min(candies, i + 1)
    candies -= min(candies, i + 1)
    i += 1  # Single, consistent increment
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?


Recommended Readings

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

Load More