Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1580. Put Boxes Into the Warehouse II - Leetcode Solution

Problem Description

The Leetcode problem Put Boxes Into the Warehouse II presents you with two arrays:

  • boxes: an array where boxes[i] is the height of the ith box.
  • warehouse: an array where warehouse[j] is the height of the jth room in the warehouse, arranged from left to right.
The challenge is to put as many boxes as possible into the warehouse, following these rules:
  • You may rearrange the order of the boxes in any way you like.
  • Each room in the warehouse can hold at most one box, and each box can be used at most once.
  • For a box to be placed in a room at position j, it must be pushed in from the leftmost end (position 0) and pass through all rooms 0 to j-1. If a box is too tall for any room it passes through, it cannot be placed in room j.
  • The goal is to maximize the number of boxes placed in the warehouse.
Key constraints: You cannot reuse boxes, and the order of the warehouse rooms is fixed. There may be only one valid solution for a given input.

Thought Process

At first glance, it might seem reasonable to try every possible arrangement of boxes and rooms, but this is not efficient. The main challenge is that a box, to reach a certain room, must fit through all rooms before it. This means the effective height of each room is the minimum height encountered from the left up to that room.

A naive approach would be to try to fit every box into every room, but that quickly becomes too slow as the input size grows.
Instead, we realize that sorting both the boxes and the warehouse (after adjusting for bottleneck heights) allows us to use a greedy approach, always fitting the smallest available box into the smallest available room it can fit into. This way, we maximize the number of boxes placed and don't "waste" tall rooms on small boxes.

Solution Approach

Let's break down the steps for an efficient solution:

  1. Preprocess the warehouse:
    • For each room, calculate the minimum height up to that room (including itself) from the left. This ensures that a box can pass through all previous rooms to reach its position.
    • This is done by iterating from left to right and updating each room's height to be the minimum of itself and all rooms before it.
  2. Sort the boxes and the processed warehouse heights:
    • Sort boxes in ascending order so we can easily pick the smallest available box.
    • Sort the processed warehouse heights in ascending order so we can fill the smallest rooms first.
  3. Greedy matching:
    • Use two pointers: one for boxes and one for rooms.
    • For each room (from smallest to largest), try to fit the smallest remaining box that fits.
    • If a box fits, increment both pointers; otherwise, try the next larger box.
    • Count each successful placement.
This approach ensures that we maximize the number of boxes placed without "wasting" space.

Example Walkthrough

Input:
boxes = [4,3,4,1]
warehouse = [5,3,3,4,1]

Step 1: Preprocess warehouse
- Start from the left, update each warehouse room to be the minimum so far:
- warehouse: [5,3,3,3,1] (since 5, min(5,3)=3, min(3,3)=3, min(3,4)=3, min(3,1)=1)

Step 2: Sort
- boxes: [1,3,4,4]
- warehouse: [1,3,3,3,5] (after sorting)

Step 3: Greedy Placement
- Try box 1 in room 1: fits (count=1)
- Try box 3 in room 3: fits (count=2)
- Try box 4 in room 3: doesn't fit, try next room (room 3): doesn't fit, try next room (room 5): fits (count=3)

Result: Maximum 3 boxes can be placed.

Time and Space Complexity

Brute-force approach:

  • Try every permutation of boxes and warehouse positions: O(n! * m!), where n = len(boxes), m = len(warehouse). This is infeasible for large inputs.
Optimized approach:
  • Preprocessing warehouse: O(m), where m is the number of rooms.
  • Sorting boxes and processed warehouse: O(n log n + m log m).
  • Greedy matching with two pointers: O(n + m).
  • Total time complexity: O(n log n + m log m)
  • Space complexity: O(n + m) for storing sorted arrays and processed warehouse.
The optimized solution is efficient and suitable for large input sizes.

Summary

This problem is a great example of how preprocessing and greedy strategies can turn an otherwise intractable brute-force search into an efficient solution. By reducing each warehouse room to its true accessible height and sorting both boxes and rooms, we can match the smallest boxes to the smallest rooms. The key insight is to recognize how bottlenecks in the warehouse affect box placement and to always use the smallest box that fits each time. This approach is both elegant and fast, making it ideal for real-world scenarios where maximizing utilization is important.

Code Implementation

class Solution:
    def maxBoxesInWarehouse(self, boxes, warehouse):
        # Step 1: Preprocess the warehouse to minimum heights up to each room
        min_height = float('inf')
        for i in range(len(warehouse)):
            min_height = min(min_height, warehouse[i])
            warehouse[i] = min_height
        
        # Step 2: Sort boxes and warehouse
        boxes.sort()
        sorted_warehouse = sorted(warehouse)
        
        # Step 3: Greedy two-pointer placement
        res = 0
        i = 0  # pointer for boxes
        j = 0  # pointer for warehouse
        while i < len(boxes) and j < len(sorted_warehouse):
            if boxes[i] <= sorted_warehouse[j]:
                res += 1
                i += 1
                j += 1
            else:
                j += 1
        return res
        
class Solution {
public:
    int maxBoxesInWarehouse(vector<int>& boxes, vector<int>& warehouse) {
        // Step 1: Preprocess warehouse to minimum heights
        int min_height = INT_MAX;
        for (int i = 0; i < warehouse.size(); ++i) {
            min_height = min(min_height, warehouse[i]);
            warehouse[i] = min_height;
        }
        // Step 2: Sort boxes and warehouse
        sort(boxes.begin(), boxes.end());
        vector<int> sorted_warehouse = warehouse;
        sort(sorted_warehouse.begin(), sorted_warehouse.end());
        
        // Step 3: Greedy placement
        int res = 0, i = 0, j = 0;
        while (i < boxes.size() && j < sorted_warehouse.size()) {
            if (boxes[i] <= sorted_warehouse[j]) {
                ++res;
                ++i;
                ++j;
            } else {
                ++j;
            }
        }
        return res;
    }
};
        
class Solution {
    public int maxBoxesInWarehouse(int[] boxes, int[] warehouse) {
        // Step 1: Preprocess warehouse to minimum heights
        int minHeight = Integer.MAX_VALUE;
        for (int i = 0; i < warehouse.length; ++i) {
            minHeight = Math.min(minHeight, warehouse[i]);
            warehouse[i] = minHeight;
        }
        // Step 2: Sort boxes and warehouse
        Arrays.sort(boxes);
        int[] sortedWarehouse = warehouse.clone();
        Arrays.sort(sortedWarehouse);
        
        // Step 3: Greedy placement
        int res = 0, i = 0, j = 0;
        while (i < boxes.length && j < sortedWarehouse.length) {
            if (boxes[i] <= sortedWarehouse[j]) {
                res++;
                i++;
                j++;
            } else {
                j++;
            }
        }
        return res;
    }
}
        
var maxBoxesInWarehouse = function(boxes, warehouse) {
    // Step 1: Preprocess warehouse to minimum heights
    let minHeight = Infinity;
    for (let i = 0; i < warehouse.length; ++i) {
        minHeight = Math.min(minHeight, warehouse[i]);
        warehouse[i] = minHeight;
    }
    // Step 2: Sort boxes and warehouse
    boxes.sort((a, b) => a - b);
    let sortedWarehouse = warehouse.slice().sort((a, b) => a - b);
    
    // Step 3: Greedy placement
    let res = 0, i = 0, j = 0;
    while (i < boxes.length && j < sortedWarehouse.length) {
        if (boxes[i] <= sortedWarehouse[j]) {
            res++;
            i++;
            j++;
        } else {
            j++;
        }
    }
    return res;
};