Problem overview
The method maximumUnits receives boxTypes, a vector of pairs [numberOfBoxes, unitsPerBox], and an integer truckSize, the most boxes the truck can carry. You choose any boxes, at most truckSize of them, and the method returns the largest total number of units that can be loaded.
In the first example, boxTypes = [[1,3],[2,2],[3,1]] and truckSize = 4. Take the one box of 3 units, both boxes of 2 units, and one of the three boxes of 1 unit. That is 4 boxes and 3 + 4 + 1 = 8 units. In the second example, [[5,10],[2,5],[4,7],[3,9]] with truckSize = 10, the answer is 91.
Approach: fill the truck with the richest boxes first
Every box takes exactly one slot, no matter how many units it holds. So each slot should hold the best box still available. That makes the problem a natural fit for a greedy strategy: sort the box types by units per box, largest first, and load boxes from the front until the truck is full or the types run out.
Why is greedy correct here? Suppose some best loading skips a box with more units in favour of a box with fewer. Swapping the smaller box for the larger one keeps the box count the same and does not lower the total. So you can always push the solution towards "richest first" without losing anything.
The number of boxes within a type matters only as a limit. For each type, you load as many as you can: all of them, or however many slots remain.
On the second example, sorted by units per box: [5,10], [3,9], [4,7], [2,5]. Load 5 boxes of 10 units (50), then 3 of 9 (27, total 77) and the truck has 2 slots left. Of the 4 boxes with 7 units, take 2 (14), total 91, and the truck is full.
Step by step
- Sort
boxTypesin place by the second column, decreasing, usingsortwith a lambda comparator. The comparator returns true when its first argument should come before the second, soa[1] > b[1]orders by units per box, largest first. - Declare
unitsas anintset to 0 to hold the running total. - Loop over the types with a range-based
forand a loop variableboxdeclaredconst auto&. Herebox[0]is how many boxes of the type exist, andbox[1]is the units in each. - Decide how many to take:
take = min(box[0], truckSize). You can't take more boxes than exist, and you can't take more than the truck has room for. - Update the totals. Add
take * box[1]tounitsand subtracttakefromtruckSize. The parameter now means the remaining capacity. - Stop when the truck is full. If
truckSize == 0,break. The loop would also finish correctly without this, sincetakewould be 0 from then on, but there is no reason to keep going. - Return
unitsafter the loop.
The solution in C++
class Solution {
public:
int maximumUnits(vector<vector<int>>& boxTypes, int truckSize) {
sort(boxTypes.begin(), boxTypes.end(), [](const auto& a, const auto& b) {
return a[1] > b[1];
});
int units = 0;
for (const auto& box : boxTypes) {
int take = min(box[0], truckSize);
units += take * box[1];
truckSize -= take;
if (truckSize == 0) {
break;
}
}
return units;
}
};
The details specific to C++:
- The comparator.
std::sortaccepts any callable as a third argument. The lambda[](const auto& a, const auto& b) { return a[1] > b[1]; }takes two rows by reference without copying them. Its parameters are declared withauto, which makes it a generic lambda, and that is valid C++14 and later. The comparison must be strict (>, not>=). A comparator that returns true for equal elements breaks the rules of the sort and can crash it. - Sorting changes the caller's data.
boxTypesis passed asvector<vector<int>>&, so the sort reorders the vector the caller gave you. That is acceptable here. If it weren't, you would take the parameter by value or sort a copy. - Swapping rows is cheap. A
vectoris moved by exchanging its internal pointers, not by copying its elements, so sorting a vector of vectors does not copy the pairs. const auto&in the loop reads each row in place. Plainautowould copy every inner vector.minneeds both arguments to have the same type. Bothbox[0]andtruckSizeareint, so it compiles. Mixing anintand asize_twould be an error.- Overflow. Each loaded box holds at most 1,000 units, and at most 1,000,000 boxes are loaded, so
unitsnever exceeds 1,000,000,000. That fits in a signed 32-bitint, whose maximum is about 2.1 billion. Signed overflow is undefined behaviour in C++, so it is worth checking such a bound.
Complexity
Time: O(m log m), where m is the number of box types. The sort dominates. The loop is one pass of constant work per type.
Space: O(1) extra beyond the sort's own small bookkeeping. The sort works in place, and the method keeps just a few integers.
Edge cases and common mistakes
- Not enough boxes to fill the truck. With
[[1,3]]andtruckSize = 5the answer is 3. The loop ends after the only type, and the truck stays partly empty. - Sorting by the wrong column. Sorting by
box[0]orders by how many boxes there are, which has nothing to do with the value of one slot. - Sorting ascending. Using
<in the comparator loads the poorest boxes first and gives a smaller total. - Taking the whole type. Adding
box[0] * box[1]without theminwould overfill the truck when only part of a type fits. - Forgetting to reduce the capacity. If
truckSizenever shrinks, every type is taken in full. - Equal units per box. Types with the same units can come in any order. The total is the same either way, so no tie-break is needed.
Testing
[[1,3],[2,2],[3,1]]withtruckSize = 4gives 8.[[5,10],[2,5],[4,7],[3,9]]withtruckSize = 10gives 91.[[1,3]]withtruckSize = 5gives 3.