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.
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:
- Calculate the total number of apples we need to pack
- Sort the boxes by capacity in descending order (largest first)
- 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.
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 EvaluatorExample 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
Iteration | Box Capacity | Running Total Capacity | Apples Remaining | Boxes Used |
---|---|---|---|---|
Start | - | 0 | 11 | 0 |
1 | 7 | 7 | 11 - 7 = 4 | 1 |
2 | 5 | 12 | 4 - 5 = -1 โค 0 | 2 |
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 takesO(m ร log m)
time, wherem
is the length of thecapacity
array - Computing the sum of all apples takes
O(n)
time, wheren
is the length of theapple
array - The iteration through the sorted
capacity
array takesO(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 usesO(log m)
space for the recursion stack in algorithms like quicksort or mergesort (Python's Timsort) - The variable
s
and loop variables useO(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
Which technique can we use to find the middle of a linked list?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Donโt Miss This!