Maximum Units on a Truck
EasyPrompt
You are assigned to put some amount of boxes onto one truck. You are given a 2D array boxTypes, where boxTypes[i] = [numberOfBoxesi, numberOfUnitsPerBoxi]:
numberOfBoxesiis the number of boxes of typei.numberOfUnitsPerBoxiis the number of units in each box of the typei.
You are also given an integer truckSize, which is the maximum number of boxes that can be put on the truck. You can choose any boxes to put on the truck as long as the number of boxes does not exceed truckSize.
Return the maximum total number of units that can be put on the truck.
Example 1:
Input: boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4
Output: 8
Explanation: There are:
- 1 box of the first type that contains 3 units.
- 2 boxes of the second type that contain 2 units each.
- 3 boxes of the third type that contain 1 unit each.
You can take all the boxes of the first and second types, and one box of the third type.
The total number of units will be = (1 * 3) + (2 * 2) + (1 * 1) = 8.Example 2:
Input: boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10
Output: 91
Constraints:
1 <= boxTypes.length <= 10001 <= numberOfBoxesi, numberOfUnitsPerBoxi <= 10001 <= truckSize <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
This approach is based on the greedy principle that to maximize the total units, we should always prioritize loading the boxes that offer the most units. By sorting the box types in descending order based on the number of units per box, we can iteratively fill the truck with the most valuable boxes first.
Algorithm
- Sort the
boxTypes2D array in descending order based on the second element (units per box). - Initialize a variable
totalUnitsto 0. - Iterate through the sorted
boxTypesarray. - For each
boxType, get thenumberOfBoxesandunitsPerBox. - Calculate the number of boxes to load for the current type:
boxesToTake = min(numberOfBoxes, remaining truckSize). - Add the units to the total:
totalUnits += boxesToTake * unitsPerBox. - Update the remaining truck size:
truckSize -= boxesToTake. - If
truckSizebecomes 0, break the loop. - Return
totalUnits.
Walkthrough
The core idea is to be greedy. We want to load the boxes that give us the maximum number of units.
First, we sort the boxTypes array. The sorting criterion is the number of units per box (boxTypes[i][1]) in descending order. This ensures that we consider the most "unit-dense" boxes first.
After sorting, we iterate through the sorted list of box types.
In each iteration, we check the remaining capacity of the truck (truckSize).
We take as many boxes of the current type as we can. The number of boxes to take is the minimum of the available boxes of that type and the remaining truck capacity.
We add the units from the loaded boxes to our total count and decrease the truckSize by the number of boxes we loaded.
We continue this process until we have either considered all box types or the truck is full (truckSize is 0).
Finally, we return the total accumulated units.
import java.util.Arrays;import java.util.Comparator; class Solution { public int maximumUnits(int[][] boxTypes, int truckSize) { // Sort the boxTypes array in descending order of units per box. Arrays.sort(boxTypes, (a, b) -> b[1] - a[1]); int totalUnits = 0; for (int[] boxType : boxTypes) { int numberOfBoxes = boxType[0]; int unitsPerBox = boxType[1]; // Determine how many boxes of this type to take. int boxesToTake = Math.min(numberOfBoxes, truckSize); totalUnits += boxesToTake * unitsPerBox; truckSize -= boxesToTake; // If the truck is full, we can stop. if (truckSize == 0) { break; } } return totalUnits; }}Complexity
Time
O(N log N), where N is the number of box types (`boxTypes.length`). The sorting step dominates the time complexity. The subsequent loop runs at most N times.
Space
O(log N) or O(N), depending on the implementation of the sorting algorithm used. In Java, `Arrays.sort` for objects uses TimSort, which requires O(N) space in the worst case.
Trade-offs
Pros
Relatively simple and intuitive to implement.
Correctly solves the problem by following a greedy strategy.
Cons
The O(N log N) time complexity from sorting is not the most optimal solution possible for this problem's constraints.
This approach improves upon the sorting method by recognizing that the number of units per box is constrained to a small range (1 to 1000). Instead of a general-purpose comparison sort, we can use a more efficient, non-comparison-based sorting technique like Counting Sort (or Bucket Sort). This allows us to group boxes by their unit count and process them in descending order of units without an O(N log N) sorting overhead.
Algorithm
- Create an integer array
bucketof size 1001, initialized to zeros. - Iterate through the
boxTypesarray. For each[numberOfBoxes, unitsPerBox], addnumberOfBoxestobucket[unitsPerBox]. - Initialize
totalUnitsto 0. - Iterate from
i = 1000down to 1. - If
bucket[i]is greater than 0: a. Calculate the number of boxes to load:boxesToTake = min(bucket[i], remaining truckSize). b. Add the units to the total:totalUnits += boxesToTake * i. c. Update the remaining truck size:truckSize -= boxesToTake. d. IftruckSizebecomes 0, break the loop. - Return
totalUnits.
Walkthrough
The key observation is that numberOfUnitsPerBox is between 1 and 1000. This limited range allows for a linear time sorting algorithm.
We create an array, let's call it bucket, of size 1001. bucket[i] will store the total count of boxes that contain i units each.
We iterate through the input boxTypes. For each [numberOfBoxes, unitsPerBox], we update our bucket: bucket[unitsPerBox] += numberOfBoxes. After this step, bucket effectively stores all boxes, grouped and counted by their unit value.
Next, we iterate through our bucket array, but in reverse order (from 1000 down to 1), because we want to load the boxes with the most units first.
For each unit value i, we check if bucket[i] has any boxes.
If it does, we determine how many boxes to take: boxesToTake = Math.min(truckSize, bucket[i]).
We update our totalUnits by boxesToTake * i and decrease truckSize by boxesToTake.
If truckSize reaches 0, we have filled the truck, and we can stop and return the result.
This method avoids the O(N log N) sort and achieves a linear time complexity.
class Solution { public int maximumUnits(int[][] boxTypes, int truckSize) { // Create a bucket array to count boxes for each unit value. // The maximum number of units per box is 1000. int[] bucket = new int[1001]; for (int[] boxType : boxTypes) { int numberOfBoxes = boxType[0]; int unitsPerBox = boxType[1]; bucket[unitsPerBox] += numberOfBoxes; } int totalUnits = 0; // Iterate from the highest unit count down to the lowest. for (int units = 1000; units >= 1; units--) { // If there are boxes with this unit count. if (bucket[units] > 0) { // Determine how many boxes of this type to take. int boxesToTake = Math.min(bucket[units], truckSize); totalUnits += boxesToTake * units; truckSize -= boxesToTake; // If the truck is full, we can stop. if (truckSize == 0) { break; } } } return totalUnits; }}Complexity
Time
O(N + M), where N is the number of box types and M is the maximum possible units per box (1001). The first loop takes O(N) to populate the buckets. The second loop takes O(M) to iterate through the buckets. Since M is a constant, the overall complexity is linear, O(N).
Space
O(M), for the bucket array, where M is the maximum possible units per box (1001). Since M is a constant, this is considered O(1) constant space.
Trade-offs
Pros
Highly efficient with linear time complexity, which is better than the sorting approach.
Constant space complexity as the size of the bucket array is fixed.
Cons
This approach is only efficient because the range of
unitsPerBoxis small and known. If the range were very large, this method would become impractical due to high space requirements.
Solutions
Solution
class Solution {public int maximumUnits(int[][] boxTypes, int truckSize) { Arrays.sort(boxTypes, (a, b)->b[1] - a[1]); int ans = 0; for (var e : boxTypes) { int a = e[0], b = e[1]; ans += b * Math.min(truckSize, a); truckSize -= a; if (truckSize <= 0) { break; } } return ans; }}class Solution {public: int maximumUnits(vector<vector<int>> &boxTypes, int truckSize) { sort(boxTypes.begin(), boxTypes.end(), [](auto &a, auto &b) { return a[1] > b[1]; }); int ans = 0; for (auto &e : boxTypes) { int a = e[0], b = e[1]; ans += b * min(truckSize, a); truckSize -= a; if (truckSize <= 0) break; } return ans; }};class Solution: def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int: ans = 0 for a, b in sorted(boxTypes, key=lambda x: - x[1]): ans += b * min(truckSize, a) truckSize -= a if truckSize <= 0: break return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.