231 lines
9.7 KiB
YAML
231 lines
9.7 KiB
YAML
title: Building Boxes
|
|
slug: building-boxes
|
|
difficulty: hard
|
|
leetcode_id: 1739
|
|
leetcode_url: https://leetcode.com/problems/building-boxes/
|
|
categories:
|
|
- math
|
|
- binary-search
|
|
patterns:
|
|
- slug: binary-search
|
|
is_optimal: true
|
|
- slug: greedy
|
|
is_optimal: false
|
|
|
|
function_signature: "def minimum_boxes(n: int) -> int:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { n: 3 }
|
|
expected: 3
|
|
- input: { n: 4 }
|
|
expected: 3
|
|
- input: { n: 10 }
|
|
expected: 6
|
|
hidden:
|
|
- input: { n: 1 }
|
|
expected: 1
|
|
- input: { n: 2 }
|
|
expected: 2
|
|
- input: { n: 5 }
|
|
expected: 4
|
|
- input: { n: 6 }
|
|
expected: 4
|
|
- input: { n: 7 }
|
|
expected: 5
|
|
- input: { n: 20 }
|
|
expected: 10
|
|
|
|
description: |
|
|
You have a cubic storeroom where the width, length, and height of the room are all equal to `n` units. You are asked to place `n` boxes in this room where each box is a cube of unit side length. There are however some rules to placing the boxes:
|
|
|
|
- You can place the boxes anywhere on the floor.
|
|
- If box `x` is placed on top of box `y`, then each side of the four vertical sides of box `y` **must** either be adjacent to another box or to a wall.
|
|
|
|
Given an integer `n`, return *the **minimum** possible number of boxes touching the floor*.
|
|
|
|
constraints: |
|
|
- `1 <= n <= 10^9`
|
|
|
|
examples:
|
|
- input: "n = 3"
|
|
output: "3"
|
|
explanation: "Place three boxes in the corner of the room, all touching the floor."
|
|
- input: "n = 4"
|
|
output: "3"
|
|
explanation: "Place three boxes on the floor in the corner, with one box stacked on top of a corner box (which has all four sides supported by walls/boxes)."
|
|
- input: "n = 10"
|
|
output: "6"
|
|
explanation: "Place boxes in a stepped pyramid pattern in the corner. Six boxes touch the floor, supporting four more boxes above them."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Imagine you're stacking boxes in the corner of a room. The corner is special because it provides two walls as support. A box can only support another box on top if all four of its vertical sides are covered — either by walls or other boxes.
|
|
|
|
Think of it like building a **stepped pyramid** in a corner. The optimal strategy is to build "layers" where:
|
|
- Layer 1 (bottom floor): boxes form a triangular staircase pattern in the corner
|
|
- Layer 2: boxes sit on top of layer 1, also forming a smaller triangle
|
|
- And so on...
|
|
|
|
The key insight is recognising that a complete pyramid of height `h` has:
|
|
- **Floor boxes**: `1 + 2 + 3 + ... + h = h(h+1)/2` (triangular number)
|
|
- **Total boxes**: `1 + 3 + 6 + ... + h(h+1)/2` (sum of triangular numbers = tetrahedral number)
|
|
|
|
The total boxes in a complete pyramid is `h(h+1)(h+2)/6` — this is the h<sup>th</sup> **tetrahedral number**.
|
|
|
|
Our strategy: find the largest complete pyramid that fits within `n` boxes, then add floor boxes one-by-one until we can fit all `n` boxes. Each additional floor box lets us stack a "column" of boxes on top of the existing structure.
|
|
|
|
approach: |
|
|
We solve this using **Mathematical Analysis + Greedy Addition**:
|
|
|
|
**Step 1: Find the largest complete pyramid**
|
|
|
|
- A complete pyramid of height `h` contains `h(h+1)(h+2)/6` total boxes
|
|
- Use binary search or iteration to find the largest `h` where this formula gives `<= n`
|
|
- Let `total` be the number of boxes in this complete pyramid
|
|
- Let `floor` be the floor boxes in this pyramid: `h(h+1)/2`
|
|
|
|
|
|
|
|
**Step 2: Handle remaining boxes**
|
|
|
|
- Calculate `remaining = n - total` (boxes left to place)
|
|
- If `remaining == 0`, return `floor`
|
|
|
|
|
|
|
|
**Step 3: Add floor boxes one at a time**
|
|
|
|
- Each new floor box at position `k` allows us to place `k` additional boxes (a column of height `k`)
|
|
- Add floor boxes with `k = 1, 2, 3, ...` until the cumulative column heights cover `remaining`
|
|
- This is because the k<sup>th</sup> additional floor box can support a column of `k` boxes above it
|
|
|
|
|
|
|
|
**Step 4: Return the total floor boxes**
|
|
|
|
- Return `floor + (number of additional floor boxes needed)`
|
|
|
|
|
|
|
|
The greedy addition works because adding floor boxes in order (1, 2, 3, ...) maximises the boxes we can stack per floor box added.
|
|
|
|
common_pitfalls:
|
|
- title: Ignoring the Corner Constraint
|
|
description: |
|
|
Without using the corner, you'd need far more floor boxes. The corner provides two walls, meaning boxes placed against both walls can immediately support boxes on top.
|
|
|
|
The optimal solution always uses the corner to minimise floor contact.
|
|
wrong_approach: "Placing boxes in the middle of the room"
|
|
correct_approach: "Build a pyramid in the corner using wall support"
|
|
|
|
- title: Integer Overflow with Large n
|
|
description: |
|
|
With `n` up to `10^9`, computing `h(h+1)(h+2)` can overflow 32-bit integers. In Python this isn't an issue, but in other languages you need to use 64-bit integers or be careful with the order of operations.
|
|
|
|
Also, the maximum pyramid height for `n = 10^9` is around 1817, so iteration is feasible.
|
|
wrong_approach: "Using 32-bit integers for intermediate calculations"
|
|
correct_approach: "Use 64-bit integers or Python's arbitrary precision"
|
|
|
|
- title: Off-by-One in Additional Boxes
|
|
description: |
|
|
When adding floor boxes after a complete pyramid, the k<sup>th</sup> additional floor box supports a column of `k` boxes (including itself as the base counted towards remaining). Be careful about whether you're counting the floor box itself.
|
|
|
|
The cumulative sum `1 + 2 + ... + k = k(k+1)/2` should cover `remaining`.
|
|
wrong_approach: "Miscounting how many boxes each floor box supports"
|
|
correct_approach: "The k-th new floor box adds capacity for k boxes total"
|
|
|
|
key_takeaways:
|
|
- "**Triangular and tetrahedral numbers**: Complete pyramids follow `h(h+1)(h+2)/6` — recognising this pattern is key"
|
|
- "**Greedy completion**: After the largest complete pyramid, add floor boxes greedily to cover remaining capacity"
|
|
- "**Geometric reasoning**: Visualising 3D stacking as layered 2D triangles simplifies the math"
|
|
- "**Binary search for inverse functions**: Finding the largest `h` where `f(h) <= n` is a classic binary search application"
|
|
|
|
time_complexity: "O(n^(1/3)). The pyramid height h is proportional to the cube root of n, and we iterate up to h steps."
|
|
space_complexity: "O(1). Only a few variables for tracking the pyramid dimensions and remaining boxes."
|
|
|
|
solutions:
|
|
- approach_name: Mathematical Analysis
|
|
is_optimal: true
|
|
code: |
|
|
def minimum_boxes(n: int) -> int:
|
|
# Step 1: Find the largest complete pyramid height
|
|
# Tetrahedral number: h(h+1)(h+2)/6
|
|
h = 0
|
|
total = 0 # Total boxes in complete pyramid
|
|
floor = 0 # Floor boxes in complete pyramid
|
|
|
|
# Build complete layers until we exceed n
|
|
while total + (h + 1) * (h + 2) // 2 <= n:
|
|
h += 1
|
|
# Add triangular number h(h+1)/2 to floor
|
|
floor += h
|
|
# Total boxes = sum of triangular numbers = h(h+1)(h+2)/6
|
|
total += h * (h + 1) // 2
|
|
|
|
# Step 2: Add individual floor boxes for remaining capacity
|
|
remaining = n - total
|
|
extra_floor = 0
|
|
capacity = 0 # How many boxes we can add with extra floor boxes
|
|
|
|
# Each new floor box k allows stacking k boxes
|
|
while capacity < remaining:
|
|
extra_floor += 1
|
|
capacity += extra_floor # The k-th floor box adds k capacity
|
|
|
|
return floor + extra_floor
|
|
explanation: |
|
|
**Time Complexity:** O(n^(1/3)) — The pyramid height is O(n^(1/3)), and we iterate through layers and additional floor boxes.
|
|
|
|
**Space Complexity:** O(1) — Only constant extra space for variables.
|
|
|
|
We first build the largest complete pyramid possible (where each layer is a triangular arrangement). Then we greedily add floor boxes one by one. Each additional floor box at position k lets us add a column of k boxes. We stop when we have enough capacity for all n boxes.
|
|
|
|
- approach_name: Binary Search
|
|
is_optimal: false
|
|
code: |
|
|
def minimum_boxes(n: int) -> int:
|
|
# Binary search for the largest complete pyramid height
|
|
def tetrahedral(h: int) -> int:
|
|
"""Total boxes in a complete pyramid of height h."""
|
|
return h * (h + 1) * (h + 2) // 6
|
|
|
|
def triangular(k: int) -> int:
|
|
"""k-th triangular number."""
|
|
return k * (k + 1) // 2
|
|
|
|
# Find largest h where tetrahedral(h) <= n
|
|
lo, hi = 0, 2000 # Max h for n=10^9 is ~1817
|
|
while lo < hi:
|
|
mid = (lo + hi + 1) // 2
|
|
if tetrahedral(mid) <= n:
|
|
lo = mid
|
|
else:
|
|
hi = mid - 1
|
|
|
|
h = lo
|
|
total = tetrahedral(h)
|
|
floor = triangular(h)
|
|
remaining = n - total
|
|
|
|
if remaining == 0:
|
|
return floor
|
|
|
|
# Binary search for minimum extra floor boxes needed
|
|
# k extra floor boxes give capacity 1+2+...+k = k(k+1)/2
|
|
lo, hi = 1, 2000
|
|
while lo < hi:
|
|
mid = (lo + hi) // 2
|
|
if triangular(mid) >= remaining:
|
|
hi = mid
|
|
else:
|
|
lo = mid + 1
|
|
|
|
return floor + lo
|
|
explanation: |
|
|
**Time Complexity:** O(log n) — Two binary searches with O(log(n^(1/3))) = O(log n) complexity.
|
|
|
|
**Space Complexity:** O(1) — Only constant extra space.
|
|
|
|
This approach uses binary search twice: first to find the largest complete pyramid, then to find the minimum additional floor boxes needed. While asymptotically faster, the constants are similar since n^(1/3) for n=10^9 is only ~1000. The iterative approach is often clearer and equally fast in practice.
|