Problem overview
The method maximumUnits receives boxTypes, a list of [numberOfBoxes, numberOfUnitsPerBox] pairs, and truckSize, the maximum number of boxes the truck can hold. You choose which boxes to load, and the method returns the largest possible total of units.
In the first example, boxTypes = [[1, 3], [2, 2], [3, 1]] and truckSize = 4. Take the one box with 3 units, then both boxes with 2 units each, then one of the boxes with 1 unit. The total is 3 + 4 + 1 = 8. In the second example, boxTypes = [[5, 10], [2, 5], [4, 7], [3, 9]] and truckSize = 10: the answer is 91.
Approach: load the densest boxes first
Every box takes exactly one slot on the truck, no matter how many units it holds. So there is no trade-off to weigh. Given a free slot, you should always fill it with the box that has the most units. That is a greedy argument: if an optimal load ever skipped a richer box for a poorer one, swapping them would not lower the total.
The plan is to sort the box types by units per box, largest first, and then take as many boxes as possible from each type in turn until the truck is full.
On the second example, sorting by units per box gives [[5, 10], [3, 9], [4, 7], [2, 5]]. The truck holds 10 boxes:
- Take all 5 boxes of 10 units: 50 units, 5 slots left.
- Take all 3 boxes of 9 units: 27 units, 2 slots left.
- Take 2 of the 4 boxes of 7 units: 14 units, 0 slots left.
The total is 50 + 27 + 14 = 91.
Why sorting by box count fails
It is tempting to load the biggest groups first, since they fill the truck quickly. On the second example it gives 50 + 28 + 9 = 87 instead of 91, because it loads the 7-unit boxes before the 9-unit ones. A smaller case makes the point sharper: with [[5, 1], [1, 100]] and truckSize = 5, loading the five-box group first yields 5 units, while taking the single 100-unit box first and four 1-unit boxes yields 104. Slots are the scarce resource, so units per box is the only thing that ranks the choices.
Because boxes of the same type are interchangeable, taking a type partly is fine. If three boxes fit and the type has ten, you take three. There is never a reason to skip ahead and leave room unused while better or equal boxes remain.
Step by step
- Sort
boxTypesin place with a comparator that orders by the second element in decreasing order:b[1] - a[1]. - Declare
unitswithlet, starting at 0, for the running total. - Loop over the sorted pairs with
for...of, destructuring each intoboxesandunitsPerBox. - Compute how many boxes to take. It is the smaller of the boxes available and the room left, so
Math.min(boxes, truckSize). - Add
take * unitsPerBoxtounits. - Subtract
takefromtruckSizeso the variable always holds the free room. If it hits 0,break: nothing more fits. - Return
units.
The solution in TypeScript
class Solution {
maximumUnits(boxTypes: number[][], truckSize: number): number {
boxTypes.sort((a, b) => b[1] - a[1]);
let units = 0;
for (const [boxes, unitsPerBox] of boxTypes) {
const take = Math.min(boxes, truckSize);
units += take * unitsPerBox;
truckSize -= take;
if (truckSize === 0) {
break;
}
}
return units;
}
}
TypeScript details to notice:
- The comparator.
sortcalls it with two elements, here two[boxes, unitsPerBox]arrays. A negative result putsafirst, sob[1] - a[1]puts the larger units-per-box first. Without a comparator,sortwould convert elements to strings and compare those, which is meaningless for pairs of numbers. sortchanges the caller's array. After the call on the second example, the caller'sboxTypesreads[[5,10],[3,9],[4,7],[2,5]]. Copy first with[...boxTypes]if the original order matters.- Destructuring.
const [boxes, unitsPerBox]unpacks the two elements of each pair. Usingconstis fine, because these names are never reassigned within an iteration. - Parameters can be reassigned.
truckSize -= takechanges the local parameter only. The caller's number is untouched, because numbers are passed by value. - No overflow. The constraints keep the total under 1000 * 1000 * 1000, far below the 2^53 limit up to which
numberholds integers exactly.
Complexity
Let m be the number of box types.
Time: O(m log m). Sorting dominates. The loop that follows visits each type at most once.
Space: O(1) extra, apart from whatever the engine uses internally to sort. The solution reorders the input in place and builds nothing new.
The truck size can be as large as a million, but the loop never counts individual boxes: it takes a whole type at a time with Math.min, so the work depends on the number of types only.
Edge cases and common mistakes
- The truck is bigger than all boxes together.
[[5, 10]]withtruckSize = 100returns 50. The loop ends without ever reaching zero room. - Few slots.
[[5, 10], [1, 100]]withtruckSize = 1returns 100: the sort puts the dense single box first. - Sorting by the wrong field. Sorting by the number of boxes,
a[0], is a common slip. The count says nothing about value per slot. - Sorting ascending. Writing
a[1] - b[1]loads the poorest boxes first and gives a smaller total. - Taking a whole type regardless of room. Without the
Math.min, the first example would load all 3 boxes of 1 unit and exceed the capacity. - Leaving out the
break. The result would still be right, sincetakewould be 0 for every remaining type. Thebreakonly saves iterations.
Testing
[[1, 3], [2, 2], [3, 1]]withtruckSize = 4gives8.[[5, 10], [2, 5], [4, 7], [3, 9]]withtruckSize = 10gives91.[[5, 10]]withtruckSize = 100gives50.[[5, 10], [1, 100]]withtruckSize = 1gives100.