1710. Maximum Units on a Truck
Problem Description
You need to load boxes onto a truck to maximize the total number of units transported. You're given a 2D array boxTypes
where each element boxTypes[i] = [numberOfBoxes_i, numberOfUnitsPerBox_i]
contains:
numberOfBoxes_i
: the available quantity of boxes of typei
numberOfUnitsPerBox_i
: the number of units contained in each box of typei
You also have a truckSize
parameter that represents the maximum number of boxes the truck can carry.
Your task is to select boxes to load onto the truck such that:
- The total number of boxes doesn't exceed
truckSize
- The total number of units on the truck is maximized
You can choose any combination of available boxes from different types as long as you don't exceed the truck's capacity.
For example, if you have boxTypes = [[1,3],[2,2],[3,1]]
and truckSize = 4
:
- Type 1: 1 box with 3 units each
- Type 2: 2 boxes with 2 units each
- Type 3: 3 boxes with 1 unit each
The optimal strategy would be to take 1 box of type 1 (3 units) and 2 boxes of type 2 (4 units), plus 1 box of type 3 (1 unit), giving you 4 boxes total with 8 units.
The solution uses a greedy approach by sorting the box types by units per box in descending order, then filling the truck with boxes that have the most units first until the truck is full or all boxes are loaded.
Intuition
The key insight is that we want to maximize the total units while being constrained by the number of boxes we can carry. Since each box takes up exactly one slot on the truck regardless of how many units it contains, we should prioritize boxes with the highest unit density (units per box).
Think of it like packing for a trip with limited luggage space - you'd want to pack the most valuable items first. Here, "value" is the number of units each box contains.
This naturally leads to a greedy strategy: always pick the boxes with the most units first. If we have boxes with 5 units each and boxes with 1 unit each, and we can only carry a limited number of boxes, we'd obviously want to fill our truck with the 5-unit boxes before considering the 1-unit boxes.
The approach becomes clear:
- Sort all box types by their units per box in descending order (highest units first)
- Greedily take as many boxes as possible from each type, starting with the highest-value boxes
- Keep track of remaining truck capacity as we load boxes
- Stop when either the truck is full or we've considered all available boxes
For each box type, we take min(truckSize, numberOfBoxes)
- either all available boxes of that type or just enough to fill the remaining truck space, whichever is smaller. This ensures we maximize units while respecting both the truck's capacity and the available inventory.
The formula ans += unitsPerBox * min(truckSize, numberOfBoxes)
captures this perfectly - we add the total units from the boxes we can actually load.
Solution Approach
The implementation follows a greedy algorithm with sorting:
Step 1: Sort the boxes by unit value
sorted(boxTypes, key=lambda x: -x[1])
We sort boxTypes
in descending order based on the number of units per box (second element of each subarray). The negative sign -x[1]
ensures descending order - boxes with the most units come first.
Step 2: Greedily load boxes onto the truck
for a, b in sorted(boxTypes, key=lambda x: -x[1]):
ans += b * min(truckSize, a)
truckSize -= a
if truckSize <= 0:
break
For each box type [a, b]
where:
a
= number of available boxes of this typeb
= units per box of this type
We iterate through the sorted list and:
-
Calculate units to add:
b * min(truckSize, a)
- If we have more boxes available (
a
) than remaining truck capacity (truckSize
), we can only taketruckSize
boxes - Otherwise, we take all
a
available boxes - Multiply the number of boxes taken by units per box (
b
) to get total units
- If we have more boxes available (
-
Update remaining capacity:
truckSize -= a
- Subtract the number of boxes of this type from remaining capacity
- Note:
truckSize
can become negative, but we handle this in the next step
-
Check termination condition:
if truckSize <= 0: break
- If truck is full or overfilled, stop processing
- When
truckSize
becomes 0 or negative, we've used all available space
The algorithm ensures optimal selection by always choosing the most valuable boxes first, and the min()
function handles the edge case where we might not need all boxes of a particular type to fill the truck.
Time Complexity: O(n log n)
for sorting, where n
is the number of box types
Space Complexity: O(1)
if we don't count the sorted array (or O(n)
if we do)
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.
Input: boxTypes = [[5,10],[2,5],[4,7],[3,9]]
, truckSize = 10
Step 1: Sort by units per box (descending)
Original array with annotations:
[5,10]
โ 5 boxes with 10 units each[2,5]
โ 2 boxes with 5 units each[4,7]
โ 4 boxes with 7 units each[3,9]
โ 3 boxes with 9 units each
After sorting by units per box (second element, descending):
[[5,10], [3,9], [4,7], [2,5]]
Step 2: Greedily load boxes
Initialize: ans = 0
, truckSize = 10
Iteration 1: [5,10]
(5 boxes, 10 units each)
- Can take:
min(truckSize=10, boxes=5) = 5
boxes - Add units:
ans += 10 * 5 = 50
- Update capacity:
truckSize = 10 - 5 = 5
- Continue (truck not full)
Iteration 2: [3,9]
(3 boxes, 9 units each)
- Can take:
min(truckSize=5, boxes=3) = 3
boxes - Add units:
ans += 9 * 3 = 27
(total: 77) - Update capacity:
truckSize = 5 - 3 = 2
- Continue (truck not full)
Iteration 3: [4,7]
(4 boxes, 7 units each)
- Can take:
min(truckSize=2, boxes=4) = 2
boxes - Add units:
ans += 7 * 2 = 14
(total: 91) - Update capacity:
truckSize = 2 - 4 = -2
- Stop (truckSize โค 0)
Result: Maximum units = 91
We loaded:
- 5 boxes with 10 units each = 50 units
- 3 boxes with 9 units each = 27 units
- 2 boxes with 7 units each = 14 units
- Total: 10 boxes carrying 91 units
By prioritizing high-value boxes first, we maximized the total units within the truck's 10-box capacity.
Solution Implementation
1class Solution:
2 def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int:
3 # Initialize total units counter
4 total_units = 0
5
6 # Sort boxes by units per box in descending order (greedy approach)
7 # Each element is [number_of_boxes, units_per_box]
8 sorted_boxes = sorted(boxTypes, key=lambda box: -box[1])
9
10 # Iterate through sorted boxes, taking boxes with most units first
11 for num_boxes, units_per_box in sorted_boxes:
12 # Take as many boxes as possible (limited by available boxes or remaining truck capacity)
13 boxes_to_take = min(truckSize, num_boxes)
14
15 # Add units from these boxes to total
16 total_units += units_per_box * boxes_to_take
17
18 # Reduce remaining truck capacity
19 truckSize -= boxes_to_take
20
21 # If truck is full, stop loading
22 if truckSize <= 0:
23 break
24
25 return total_units
26
1class Solution {
2 public int maximumUnits(int[][] boxTypes, int truckSize) {
3 // Sort boxes by units per box in descending order (greedy approach)
4 // boxTypes[i] = [numberOfBoxes, unitsPerBox]
5 Arrays.sort(boxTypes, (a, b) -> b[1] - a[1]);
6
7 // Track total units loaded onto truck
8 int totalUnits = 0;
9
10 // Iterate through sorted box types
11 for (int[] boxType : boxTypes) {
12 int numberOfBoxes = boxType[0];
13 int unitsPerBox = boxType[1];
14
15 // Take as many boxes of current type as possible
16 // Limited by either available boxes or remaining truck capacity
17 int boxesToTake = Math.min(truckSize, numberOfBoxes);
18
19 // Add units from taken boxes to total
20 totalUnits += unitsPerBox * boxesToTake;
21
22 // Reduce remaining truck capacity
23 truckSize -= boxesToTake;
24
25 // If truck is full, stop loading
26 if (truckSize <= 0) {
27 break;
28 }
29 }
30
31 return totalUnits;
32 }
33}
34
1class Solution {
2public:
3 int maximumUnits(vector<vector<int>>& boxTypes, int truckSize) {
4 // Sort boxes by units per box in descending order (greedy approach)
5 // Each boxType contains [numberOfBoxes, unitsPerBox]
6 sort(boxTypes.begin(), boxTypes.end(), [](const auto& a, const auto& b) {
7 return a[1] > b[1];
8 });
9
10 int totalUnits = 0;
11
12 // Iterate through sorted box types and load boxes onto truck
13 for (const auto& boxType : boxTypes) {
14 int numberOfBoxes = boxType[0];
15 int unitsPerBox = boxType[1];
16
17 // Take as many boxes as possible (limited by available boxes or truck capacity)
18 int boxesToTake = min(truckSize, numberOfBoxes);
19
20 // Add units from taken boxes to total
21 totalUnits += unitsPerBox * boxesToTake;
22
23 // Update remaining truck capacity
24 truckSize -= boxesToTake;
25
26 // If truck is full, stop loading
27 if (truckSize <= 0) {
28 break;
29 }
30 }
31
32 return totalUnits;
33 }
34};
35
1/**
2 * Calculates the maximum number of units that can be loaded onto a truck
3 * @param boxTypes - Array of [numberOfBoxes, unitsPerBox] pairs
4 * @param truckSize - Maximum number of boxes the truck can carry
5 * @returns Maximum total units that can be loaded
6 */
7function maximumUnits(boxTypes: number[][], truckSize: number): number {
8 // Sort boxes by units per box in descending order (greedy approach: take boxes with more units first)
9 boxTypes.sort((boxA: number[], boxB: number[]) => boxB[1] - boxA[1]);
10
11 let totalUnits: number = 0;
12 let remainingCapacity: number = truckSize;
13
14 // Iterate through sorted box types
15 for (const boxType of boxTypes) {
16 const numberOfBoxes: number = boxType[0];
17 const unitsPerBox: number = boxType[1];
18
19 // Take as many boxes of this type as possible (limited by availability or truck capacity)
20 const boxesToTake: number = Math.min(remainingCapacity, numberOfBoxes);
21
22 // Add units from taken boxes to total
23 totalUnits += boxesToTake * unitsPerBox;
24
25 // Update remaining truck capacity
26 remainingCapacity -= boxesToTake;
27
28 // Stop if truck is full
29 if (remainingCapacity <= 0) {
30 break;
31 }
32 }
33
34 return totalUnits;
35}
36
Time and Space Complexity
Time Complexity: O(n ร log n)
, where n
is the length of the array boxTypes
. The dominant operation is the sorting of the boxTypes
array using sorted()
, which takes O(n ร log n)
time. After sorting, the code iterates through the sorted array once, which takes O(n)
time. Since O(n ร log n) + O(n) = O(n ร log n)
, the overall time complexity is O(n ร log n)
.
Space Complexity: O(n)
, where n
is the length of the array boxTypes
. The sorted()
function in Python creates a new sorted list rather than sorting in-place, which requires O(n)
additional space to store the sorted copy of the input array. The other variables (ans
, a
, b
, truckSize
) use constant space O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly updating truck capacity
A common mistake is subtracting the wrong value from truckSize
. Some developers accidentally subtract num_boxes
instead of boxes_to_take
:
Incorrect:
for num_boxes, units_per_box in sorted_boxes:
boxes_to_take = min(truckSize, num_boxes)
total_units += units_per_box * boxes_to_take
truckSize -= num_boxes # Wrong! This subtracts all available boxes
if truckSize <= 0:
break
Correct:
for num_boxes, units_per_box in sorted_boxes:
boxes_to_take = min(truckSize, num_boxes)
total_units += units_per_box * boxes_to_take
truckSize -= boxes_to_take # Correct: subtract only what we took
if truckSize <= 0:
break
2. Sorting in the wrong order
Another pitfall is sorting in ascending order instead of descending order, which would load the least valuable boxes first:
Incorrect:
sorted_boxes = sorted(boxTypes, key=lambda box: box[1]) # Ascending order
Correct:
sorted_boxes = sorted(boxTypes, key=lambda box: -box[1]) # Descending order
# OR
sorted_boxes = sorted(boxTypes, key=lambda box: box[1], reverse=True)
3. Off-by-one error in termination condition
Using truckSize < 0
instead of truckSize <= 0
can cause unnecessary iterations:
Inefficient:
if truckSize < 0: # Continues even when truckSize is exactly 0 break
Efficient:
if truckSize <= 0: # Stops immediately when truck is full break
4. Not handling the partial box case correctly
Forgetting to use min()
when calculating boxes to take can lead to counting more boxes than actually fit:
Incorrect:
total_units += units_per_box * num_boxes # Always takes all available boxes
Correct:
boxes_to_take = min(truckSize, num_boxes)
total_units += units_per_box * boxes_to_take # Takes only what fits
How does merge sort divide the problem into subproblems?
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!