1739. Building Boxes
Problem Description
You are given a task to organize n
unit-sized cubic boxes into a room that is also a cube with sides of length n
. While you can place the boxes in any manner on the room's floor, there's a constraint for stacking them:
- If you stack one box on top of another, the box at the bottom (
y
) must have each of its four vertical sides either adjacent to other boxes or to the room's wall.
The goal is to determine the minimum number of boxes that must be placed directly on the floor to accommodate all n
boxes under the given rules.
Intuition
To solve this problem, visualize the arrangement of boxes in layers, starting from the bottom. The key point is understanding that not all boxes need to go on the floor; they can be stacked on top of each other.
We can place the boxes in a triangular formation on each layer. The first layer will have one box, the second layer will have two additional boxes (making a triangle of 3), and this pattern continues, adding a triangular number of boxes with each new layer. Triangular numbers are given by the formula k * (k + 1) / 2
, where k
is the layer level.
First, we find out the maximum height (k
) such that when we add up all the boxes in these triangular layers (s
), it is less than or equal to n
. We do this by iteratively increasing k
and calculating the sum s
until s
plus the next triangular number would surpass n
.
Once we've established the maximum height, we calculate the total boxes up to this height using the triangular number formula, which gives us the boxes that would be touching the floor if only complete triangular layers were used.
However, we may not yet have accounted for all n
boxes—there could be some left over that don't form a complete triangular layer. To include these, we start adding them one at a time in the next layer (k + 1
), increasing the number of floor-touching boxes by 1 each time (since each new box added to the layer needs to touch the floor to maintain stability, as per the given rules), until we reach n
boxes.
The solution effectively combines these two steps: finding the maximum fully filled triangular layer, then incrementally adding the boxes that don't fit into a full triangular layer, ensuring stability and adherence to the rules specified.
Learn more about Greedy, Math and Binary Search patterns.
Solution Approach
The solution uses a simple but careful counting approach and capitalizes on the properties of triangular numbers to solve this problem efficiently.
Step 1: Maximizing the Base Layer
Initially, the variables s
and k
are set to 0. Variable s
represents the sum of boxes used so far, and k
represents the hypothetical height of the stack if it were to be composed of complete triangular layers.
In the while
loop, we check the sum s
along with the addition of the next triangular number k * (k + 1) / 2
to ensure it doesn't exceed the total number n
of boxes we have. If the condition is satisfied, it means we can place another complete triangular layer on top of the existing stack.
The line s += k * (k + 1) // 2
accumulates the total number of boxes used in these complete layers, while k += 1
moves us up to the next layer level. This loop continues until adding another triangular layer would result in s
being greater than n
.
Step 2: Completing after Maximizing
After maximizing the base with complete triangular layers, we decrement k
by 1 because the loop increments k
one time too many before exiting. At this point, ans
is set to k * (k + 1) // 2
, representing the boxes on the floor if only complete layers are used.
The next while
loop is used for adding the leftover boxes that do not form a complete triangular layer. We start adding them one by one (by incrementing s
by k
each time) and for each box added, we increase ans
by 1 since each box will touch the floor.
The line s += k
adds a box to the next layer, while ans += 1
counts the box as touching the floor. The variable k
increment k += 1
ensures that we are preparing the count for potentially adding another box on top of the previous ones in an incomplete layer.
This iteration continues until we have placed all n
boxes (s < n
), at which point we return ans
, the accumulated count of how many boxes touch the floor.
No additional data structures are used in this solution, as we only need to keep track of integer counts and sums. The elegance of the solution stems from understanding the problem's geometric nature and how a careful count of the triangular layers converts directly into an algorithm that uses only basic arithmetic operations.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with an example where n = 10
, representing the total number of boxes we need to place inside the cubic room.
Step 1: Maximizing the Base Layer
We start with s = 0
and k = 0
, with s
indicating the sum of boxes placed so far, and k
representing the height of the stack with complete triangular layers. We need to iterate and increase k
to place as many complete triangular layers as we can without exceeding the total number of boxes.
- For
k = 1
, the number of boxes used would bek * (k + 1) / 2 = 1 * (1 + 1) / 2 = 1
. Sinces + 1 <= 10
, we updates
to be1
and incrementk
to2
. - For
k = 2
, the number of boxes for this layer would be2 * (2 + 1) / 2 = 3
. Nows + 3 = 1 + 3 = 4
, which is still less than10
, so we updates
to be4
and incrementk
to3
. - For
k = 3
, the third layer would add3 * (3 + 1) / 2 = 6
boxes, making the totals + 6 = 4 + 6 = 10
, which exactly matches our total number of boxes. We updates
to10
.
Since adding another triangular layer would exceed n
, we stop the iterations here with a base layer maximized with k-1
triangular layers since the last iteration equaled n
.
The number of boxes on the floor is the sum of complete triangular layers, s = 10
in this example.
Step 2: Completing after Maximizing
We've already placed all 10
boxes in this example with the step 1 iteration, so there are no leftover boxes to add in incomplete layers. The solution avoids this step because s = n
, and we already have the required number of boxes on the floor.
Hence, the final answer, which is the number of boxes touching the floor with n = 10
, is 10
.
In this example, we have demonstrated how the solution maximizes the use of complete triangular layers to get as close as possible to the total number of boxes and then (if necessary) adds the remaining few boxes in the most efficient way adhering to the stacking rules.
Solution Implementation
1class Solution:
2 def minimum_boxes(self, total: int) -> int:
3 # Initialize the sum (s) of placed balls and the count (count_levels) of levels.
4 sum_placed_balls, count_levels = 0, 1
5
6 # Calculate the number of levels we can create while we have enough balls.
7 # The formula (count_levels * (count_levels + 1) // 2) calculates the
8 # number of balls that can be placed on a triangular level with
9 # 'count_levels' levels.
10 while sum_placed_balls + count_levels * (count_levels + 1) // 2 <= total:
11 sum_placed_balls += count_levels * (count_levels + 1) // 2
12 count_levels += 1
13
14 # Adjust for the extra count since the last addition in the while loop was not needed.
15 count_levels -= 1
16
17 # Calculate the number of boxes used to build the triangular base.
18 num_boxes_base = count_levels * (count_levels + 1) // 2
19
20 # Start adding the minimum number of boxes needed on the top level,
21 # one by one, until all balls are placed.
22 count_top_level = 1
23 while sum_placed_balls < total:
24 num_boxes_base += 1 # Each time, one more box is added to the top level.
25 sum_placed_balls += count_top_level # Keep track of the total number of balls placed.
26 count_top_level += 1 # Increment the top level counter (number of boxes you can add in the next step).
27
28 # Return the total number of boxes needed to place all balls.
29 return num_boxes_base
30
1class Solution {
2 public int minimumBoxes(int n) {
3 // Initialize the sum of total balls (s) and the layer level (k)
4 int totalBalls = 0;
5 int layerLevel = 1;
6
7 // Find the maximum layer level such that the number of balls used is less than or equal to n
8 while (totalBalls + layerLevel * (layerLevel + 1) / 2 <= n) {
9 totalBalls += layerLevel * (layerLevel + 1) / 2;
10 layerLevel++;
11 }
12
13 // Decrement layer level since the last addition went over the limit
14 layerLevel--;
15
16 // Calculate the initial number of boxes needed to stack the pyramidal structure
17 int boxesNeeded = layerLevel * (layerLevel + 1) / 2;
18
19 // Reset layerLevel to start the flat stacking process to use up leftover balls
20 layerLevel = 1;
21
22 // While there are balls remaining, stack them flat, one per each layer level
23 while (totalBalls < n) {
24 boxesNeeded++; // Increment the number of boxes as we place a new ball
25 totalBalls += layerLevel; // The number of balls increases by the current flat layer level
26 layerLevel++; // Increment the flat layer level
27 }
28
29 // Return the total number of boxes needed
30 return boxesNeeded;
31 }
32}
33
1class Solution {
2public:
3 int minimumBoxes(int totalCuboids) {
4 // Initialize the total number of full floors and the variable to count layers
5 int totalFullFloors = 0, layerCount = 1;
6
7 // Keep adding layers until the number of cuboids for the full floors
8 // surpasses the total number of cuboids we have
9 while (totalFullFloors + layerCount * (layerCount + 1) / 2 <= totalCuboids) {
10 totalFullFloors += layerCount * (layerCount + 1) / 2;
11 ++layerCount;
12 }
13
14 // After finding the last full floor, we move one layer down
15 --layerCount;
16
17 // Calculate the total number of boxes (cuboids) used to build the full floors
18 int minBoxes = layerCount * (layerCount + 1) / 2;
19
20 // Now add the minimum number of boxes (cuboids) needed to reach the exact number
21 // of total cuboids by constructing an incrementally growing pyramid
22 layerCount = 1;
23 while (totalFullFloors < totalCuboids) {
24 minBoxes++;
25 totalFullFloors += layerCount;
26 ++layerCount;
27 }
28
29 // Return the total minimum number of boxes (cuboids) needed
30 return minBoxes;
31 }
32};
33
1let totalFullFloors: number = 0;
2let layerCount: number = 1;
3
4function minimumBoxes(totalCuboids: number): number {
5 // Reset the state for each call
6 totalFullFloors = 0;
7 layerCount = 1;
8
9 // Continue adding layers of cuboids to create full floors until the total number of
10 // cuboids for the full floors matches or exceeds the target number of cuboids
11 while(totalFullFloors + layerCount * (layerCount + 1) / 2 <= totalCuboids) {
12 totalFullFloors += layerCount * (layerCount + 1) / 2;
13 layerCount++;
14 }
15
16 // After determining the highest full floor, move one layer down
17 layerCount--;
18
19 // Compute the total number of cuboids used to construct the full floors
20 let minBoxes: number = layerCount * (layerCount + 1) / 2;
21
22 // Add the minimum number of additional cuboids needed to reach the exact total
23 // by constructing an incremental step-like structure (growing pyramid)
24 layerCount = 1;
25 while (totalFullFloors < totalCuboids) {
26 minBoxes++;
27 totalFullFloors += layerCount;
28 layerCount++;
29 }
30
31 // Return the minimal number of cuboids needed to reach the total number of cuboids
32 return minBoxes;
33}
34
Time and Space Complexity
Time Complexity
The given Python code consists of two while loops that execute sequentially. Let's analyze each part:
-
The first while loop runs until the total number of boxes
s
plus thek
th triangular number is less than or equal ton
. Thek
th triangular number is computed using the formulak * (k + 1) / 2
. Sincek
increments by 1 in each iteration ands
increases quadratically, the loop runs inO(sqrt(n))
time because the number of layers that can be formed (each represented byk
) is proportional to the square root ofn
. -
The second while loop starts after the first loop ends and increments
s
byk
each iteration, which increments linearly starting from 1. This loop will run inO(sqrt(n))
time as well, as it will only go up to the number of boxes required to reachn
, which is at most what is needed to complete the current layer.
Hence, the total time complexity is O(sqrt(n)) + O(sqrt(n))
, which simplifies to O(sqrt(n))
.
Space Complexity
The space complexity of the code is O(1)
because no additional space that scales with the size of the input n
is used. Only a constant number of variables s
, k
, and ans
are used to compute the result.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the output of running the following function using input [30, 20, 10, 100, 33, 12]
?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
8
1public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
12
1class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Want a Structured Path to Master System Design Too? Don’t Miss This!