Facebook Pixel

3074. Apple Redistribution into Boxes

Problem Description

You have n packs of apples and m boxes. Each pack i contains apple[i] apples, and each box i can hold up to capacity[i] apples.

Your task is to find the minimum number of boxes needed to hold all the apples from the n packs.

Key points to understand:

  • You need to redistribute all apples from the packs into boxes
  • Apples from the same pack can be split and placed into different boxes
  • Each box has a maximum capacity that cannot be exceeded
  • You want to use as few boxes as possible

For example, if you have packs with [1, 3, 2] apples (total of 6 apples) and boxes with capacities [4, 3, 1, 5, 2], you could use the box with capacity 5 and the box with capacity 1 to hold all 6 apples, requiring just 2 boxes minimum.

The solution uses a greedy approach by sorting the boxes in descending order of capacity and selecting boxes one by one until the total capacity is enough to hold all apples. This ensures we use the largest boxes first, minimizing the total number of boxes needed.

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

Intuition

When we need to pack items into containers and minimize the number of containers used, a natural strategy is to use the largest containers first. Think about packing for a move - you'd want to use your biggest boxes first to fit as much as possible, reducing the total number of boxes needed.

The key insight here is that we don't care which specific apples go into which boxes - we only care about the total capacity. Since apples from any pack can be distributed to any box, we can treat this as a simple capacity problem: we have a total number of apples (sum(apple)) that need to fit into boxes.

To minimize the number of boxes, we should:

  1. Calculate the total number of apples we need to pack
  2. Sort the boxes by capacity in descending order (largest first)
  3. Keep selecting boxes until we have enough total capacity

Why does this greedy approach work? Because once we know the total number of apples, the problem becomes: "Select the minimum number of boxes whose combined capacity is at least the total number of apples." By always choosing the largest available box, we maximize the capacity gained with each box selection, which minimizes the total number of boxes needed.

For instance, if we need to pack 10 apples and have boxes with capacities [3, 5, 2, 7], sorting gives us [7, 5, 3, 2]. We'd pick the box with capacity 7 first (still need 3 more), then the box with capacity 5 (now we have enough with just 2 boxes). If we had picked smaller boxes first, we might have needed 3 or even all 4 boxes.

Learn more about Greedy and Sorting patterns.

Solution Approach

The implementation follows a greedy strategy with sorting:

Step 1: Sort the capacity array in descending order

capacity.sort(reverse=True)

This ensures we consider the largest boxes first. Sorting in descending order allows us to greedily select boxes with maximum capacity.

Step 2: Calculate the total number of apples

s = sum(apple)

We need to know the total number of apples across all packs to determine when we have enough box capacity.

Step 3: Iterate through sorted boxes and accumulate capacity

for i, c in enumerate(capacity, 1):
    s -= c
    if s <= 0:
        return i

The algorithm works by:

  • Starting with the total apple count s
  • For each box (in descending capacity order), we subtract its capacity from s
  • The variable s now represents the remaining apples that still need boxes
  • When s <= 0, it means we have enough capacity to hold all apples
  • We use enumerate(capacity, 1) to start counting from 1 (since we want the number of boxes, not the index)
  • Return i which represents the number of boxes we've used

Time Complexity: O(m log m) where m is the number of boxes, due to sorting the capacity array.

Space Complexity: O(1) if we sort in-place, or O(m) if the sorting algorithm requires additional space.

The greedy approach is optimal here because selecting the largest available boxes first guarantees we use the minimum number of boxes to achieve the required total capacity.

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 concrete example to illustrate the solution approach.

Given:

  • Apple packs: [3, 5, 2, 1] (we have 4 packs with these apple counts)
  • Box capacities: [2, 4, 3, 7, 5] (we have 5 boxes with these capacities)

Step 1: Calculate total apples needed

  • Total apples = 3 + 5 + 2 + 1 = 11 apples
  • We need boxes with combined capacity โ‰ฅ 11

Step 2: Sort boxes by capacity (descending)

  • Original: [2, 4, 3, 7, 5]
  • Sorted: [7, 5, 4, 3, 2]

Step 3: Greedily select boxes until we have enough capacity

IterationBox CapacityRunning Total CapacityApples RemainingBoxes Used
Start-0110
17711 - 7 = 41
25124 - 5 = -1 โ‰ค 02

Result: We need 2 boxes (the boxes with capacities 7 and 5)

The algorithm stops at iteration 2 because:

  • After taking the first box (capacity 7), we still need space for 4 more apples
  • After taking the second box (capacity 5), we have total capacity of 12, which exceeds our need of 11 apples
  • The remaining value becomes -1 (โ‰ค 0), indicating we have sufficient capacity

Note how the greedy approach ensures optimality: if we had chosen smaller boxes first (like 2, 3, 4), we would need at least 3 boxes to reach capacity 9, and still wouldn't have enough space, requiring a 4th box. By taking the largest boxes first (7 and 5), we minimize the total number of boxes to just 2.

Solution Implementation

1class Solution:
2    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
3        # Sort the capacity array in descending order to use largest boxes first
4        capacity.sort(reverse=True)
5      
6        # Calculate the total number of apples to be stored
7        total_apples = sum(apple)
8      
9        # Iterate through boxes from largest to smallest
10        for index, box_capacity in enumerate(capacity, 1):
11            # Subtract current box capacity from remaining apples
12            total_apples -= box_capacity
13          
14            # If all apples are stored (total becomes 0 or negative), return the number of boxes used
15            if total_apples <= 0:
16                return index
17
1class Solution {
2    public int minimumBoxes(int[] apple, int[] capacity) {
3        // Sort the capacity array in ascending order
4        Arrays.sort(capacity);
5      
6        // Calculate the total number of apples
7        int totalApples = 0;
8        for (int appleCount : apple) {
9            totalApples += appleCount;
10        }
11      
12        // Use boxes starting from the largest capacity
13        int boxCount = 1;
14        int n = capacity.length;
15      
16        // Keep using boxes from largest to smallest until all apples fit
17        while (true) {
18            // Subtract the capacity of the current largest unused box
19            totalApples -= capacity[n - boxCount];
20          
21            // If all apples can fit, return the number of boxes used
22            if (totalApples <= 0) {
23                return boxCount;
24            }
25          
26            // Move to the next largest box
27            boxCount++;
28        }
29    }
30}
31
1class Solution {
2public:
3    int minimumBoxes(vector<int>& apple, vector<int>& capacity) {
4        // Sort the capacity array in descending order to use boxes with largest capacity first
5        sort(capacity.rbegin(), capacity.rend());
6      
7        // Calculate the total number of apples that need to be stored
8        int totalApples = accumulate(apple.begin(), apple.end(), 0);
9      
10        // Greedily select boxes starting from the largest capacity
11        for (int boxCount = 1; ; ++boxCount) {
12            // Subtract the capacity of the current box from remaining apples
13            totalApples -= capacity[boxCount - 1];
14          
15            // If all apples can be stored, return the number of boxes used
16            if (totalApples <= 0) {
17                return boxCount;
18            }
19        }
20    }
21};
22
1/**
2 * Finds the minimum number of boxes needed to store all apples
3 * @param apple - Array containing the number of apples in each pile
4 * @param capacity - Array containing the capacity of each box
5 * @returns The minimum number of boxes needed
6 */
7function minimumBoxes(apple: number[], capacity: number[]): number {
8    // Sort boxes by capacity in descending order (largest first)
9    capacity.sort((a, b) => b - a);
10  
11    // Calculate the total number of apples to be stored
12    let totalApples = apple.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
13  
14    // Iterate through boxes, using the largest boxes first
15    for (let boxCount = 1; ; boxCount++) {
16        // Subtract the current box's capacity from remaining apples
17        totalApples -= capacity[boxCount - 1];
18      
19        // If all apples are stored, return the number of boxes used
20        if (totalApples <= 0) {
21            return boxCount;
22        }
23    }
24}
25

Time and Space Complexity

Time Complexity: O(m ร— log m + n)

The time complexity consists of two main operations:

  • Sorting the capacity array in descending order takes O(m ร— log m) time, where m is the length of the capacity array
  • Computing the sum of all apples takes O(n) time, where n is the length of the apple array
  • The iteration through the sorted capacity array takes O(m) time in the worst case

Since O(m ร— log m) dominates O(m), the overall time complexity is O(m ร— log m + n)

Space Complexity: O(log m)

The space complexity is determined by:

  • The sorting operation on the capacity array, which typically uses O(log m) space for the recursion stack in algorithms like quicksort or mergesort (Python's Timsort)
  • The variable s and loop variables use O(1) space

Therefore, the overall space complexity is O(log m)

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

Common Pitfalls

Pitfall 1: Not Checking if All Apples Can Be Stored

Issue: The code assumes that the total capacity of all boxes is sufficient to store all apples. If the sum of all box capacities is less than the total number of apples, the function will iterate through all boxes without returning a value, causing it to implicitly return None.

Example:

  • apple = [5, 5, 5] (15 apples total)
  • capacity = [2, 4, 3] (9 capacity total)
  • The function will not return any value since 9 < 15

Solution: Add a check to ensure sufficient total capacity exists:

class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        total_apples = sum(apple)
        total_capacity = sum(capacity)
      
        # Check if it's possible to store all apples
        if total_capacity < total_apples:
            return -1  # or raise an exception based on requirements
      
        capacity.sort(reverse=True)
      
        for index, box_capacity in enumerate(capacity, 1):
            total_apples -= box_capacity
            if total_apples <= 0:
                return index

Pitfall 2: Modifying the Original Capacity Array

Issue: The sort(reverse=True) operation modifies the original capacity array in-place. If the caller expects the original array to remain unchanged, this could cause unexpected behavior in the calling code.

Example:

original_capacity = [4, 3, 1, 5, 2]
result = solution.minimumBoxes([1, 3, 2], original_capacity)
# original_capacity is now [5, 4, 3, 2, 1] - modified!

Solution: Create a copy of the capacity array before sorting:

class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        # Create a copy to avoid modifying the original array
        sorted_capacity = sorted(capacity, reverse=True)
      
        total_apples = sum(apple)
      
        for index, box_capacity in enumerate(sorted_capacity, 1):
            total_apples -= box_capacity
            if total_apples <= 0:
                return index

Pitfall 3: Empty Input Arrays

Issue: The code doesn't handle edge cases where either apple or capacity arrays are empty. An empty apple array should return 0 (no boxes needed), while an empty capacity array with non-empty apple array should indicate an error.

Solution: Add input validation:

class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        # Handle edge case: no apples to store
        if not apple or sum(apple) == 0:
            return 0
      
        # Handle edge case: no boxes available
        if not capacity:
            return -1  # or raise an exception
      
        capacity.sort(reverse=True)
        total_apples = sum(apple)
      
        for index, box_capacity in enumerate(capacity, 1):
            total_apples -= box_capacity
            if total_apples <= 0:
                return index
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which technique can we use to find the middle of a linked list?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More