Skip to content

Commit 4dd91dd

Browse files
committed
Updated 1 solution
1 parent 20c5176 commit 4dd91dd

File tree

3 files changed

+54
-87
lines changed

3 files changed

+54
-87
lines changed

Chp. 08 - Recursion and Dynamic Programming/_8_13_Stack_of_Boxes/Box.java

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,25 @@
11
package _8_13_Stack_of_Boxes;
22

3-
/* Used in 9.10 */
43
public class Box {
54
int width;
65
int height;
76
int depth;
87

9-
/* Constructor */
108
Box(int w, int h, int d) {
119
width = w;
1210
height = h;
1311
depth = d;
1412
}
1513

16-
/* Constructor */
1714
Box(Box other) {
1815
width = other.width;
1916
height = other.height;
2017
depth = other.depth;
2118
}
2219

23-
/* A box can be placed on 1) an empty platform (that's what the "other == null" is for), or 2) a bigger box (in every dimension) */
24-
public boolean canPlaceAbove(Box other) {
20+
public boolean canBeAbove(Box other) {
2521
return (other == null) || (width < other.width && height < other.height && depth < other.depth);
2622
}
27-
28-
/* Book didn't have these 2 functions. I think they're needed so Boxes can be hashed correctly */
29-
30-
@Override
31-
public boolean equals(Object other) {
32-
if (other == this) {
33-
return true;
34-
} else if (other == null || !(other instanceof Box)) {
35-
return false;
36-
}
37-
Box otherBox = (Box) other;
38-
return width == otherBox.width && height == otherBox.height && depth == otherBox.depth;
39-
}
40-
41-
@Override
42-
public int hashCode() {
43-
return 2 * width + 3 * height + 5 * depth;
44-
}
4523

4624
@Override
4725
public String toString() {
Lines changed: 48 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,59 @@
11
package _8_13_Stack_of_Boxes;
22

3-
import java.util.ArrayDeque;
4-
import java.util.HashMap;
5-
6-
// Famous box stacking problem
7-
//
8-
// (Solution based off book's 2nd solution)
9-
//
10-
// Tips:
11-
// - Have "Box" class with ".canBeAbove(Box other)" function.
12-
// - Use HashMap<Box, ArrayDeque<Box>> to cache found solutions. Need to override .equals() and hashCode().
13-
// - Having "Box bottom" as a parameter is crucial but hard to think of.
14-
// - Deep copy (which is what I did) or cloning (which is what book did) is needed to avoid insidious bugs.
3+
import java.util.*;
4+
5+
// Algorithm
6+
//
7+
// 1. Create Box class with `.canBeAbove(Box other)` function.
8+
// 2. Sort Boxes in descending height order.
9+
// - This problem uses `<` instead of `<=` for Box heights. If we have 2 Boxes of equal height,
10+
// and [width, height, depth] of [4,4,4] and [7,4,7], it doesn't matter which Box comes first
11+
// after sorting, since both `Box`es can't be in the final solution. Box 1 cannot be on top of Box 2,
12+
// and Box 2 cannot be on top of Box 1.
13+
// 3. For each Box, you have 2 choices:
14+
// 1. Use the Box
15+
// 2. Don't use the Box.
16+
// 4. Recursively try both options. Return the larger height returned from these 2 options.
17+
// 5. Use a Map<Integer, Integer> cache to save solutions to sub-problems
18+
// 1. "key" is the index `i` in the list of boxes
19+
// 2. "value" is the max height possible from that Box to the end of our list of Boxes
1520

1621
public class StackOfBoxes {
17-
public static ArrayDeque<Box> buildTallestStack(ArrayDeque<Box> boxes) {
18-
return buildTallestStack(boxes, null, new HashMap<Box, ArrayDeque<Box>>());
19-
}
20-
21-
public static ArrayDeque<Box> buildTallestStack(ArrayDeque<Box> boxes, Box bottom, HashMap<Box, ArrayDeque<Box>> cache) {
22-
if (cache.containsKey(bottom)) { // checks to see if we already computed the solution
23-
/* CRUCIAL to do a deep copy. The result we return is going to be altered, and we don't want the key of an entry
24-
already placed in the HashMap to be altered. Without deep copy, we get incorrect solutions when testing */
25-
return deepCopy(cache.get(bottom));
26-
}
27-
28-
int currHeight = 0;
29-
int bestHeight = 0;
30-
ArrayDeque<Box> currStack = new ArrayDeque<>();
31-
ArrayDeque<Box> bestStack = new ArrayDeque<>();
32-
33-
for (Box box : boxes) {
34-
if (box.canPlaceAbove(bottom)) {
35-
currStack = buildTallestStack(boxes, box, cache);
36-
currHeight = stackHeight(currStack);
37-
if (currHeight > bestHeight) {
38-
bestHeight = currHeight;
39-
bestStack = currStack;
40-
}
41-
}
42-
}
43-
44-
if (bottom != null) {
45-
bestStack.addFirst(bottom);
46-
cache.put(bottom, bestStack);
22+
public static int findMaxHeight(List<Box> boxes) {
23+
if (boxes == null || boxes.size() == 0) {
24+
return 0;
4725
}
48-
49-
return bestStack;
26+
Collections.sort(boxes, (box1, box2) -> box2.height - box1.height); // sort in descending height order
27+
return findMaxHeight(boxes, 0, null, new HashMap<>(boxes.size()));
5028
}
51-
52-
private static int stackHeight(ArrayDeque<Box> boxes) {
53-
if (boxes == null) {
29+
30+
private static int findMaxHeight(List<Box> boxes, int i, Box bottom, Map<Integer, Integer> cache) {
31+
if (i >= boxes.size()) {
5432
return 0;
33+
} else if (cache.containsKey(i)) {
34+
return cache.get(i);
5535
}
56-
int height = 0;
57-
for (Box box : boxes) {
58-
height += box.height;
59-
}
60-
return height;
61-
}
62-
63-
private static ArrayDeque<Box> deepCopy(ArrayDeque<Box> boxes) {
64-
ArrayDeque<Box> result = new ArrayDeque<>();
65-
for (Box box : boxes) {
66-
result.add(new Box(box));
36+
37+
// height with this Box
38+
Box newBottom = boxes.get(i);
39+
int heightWith = 0;
40+
if (newBottom.canBeAbove(bottom)) {
41+
heightWith = newBottom.height + findMaxHeight(boxes, i + 1, newBottom, cache);
6742
}
68-
return result;
43+
44+
// height without this Box
45+
int heightWithout = findMaxHeight(boxes, i + 1, bottom, cache);
46+
47+
int max = Math.max(heightWith, heightWithout);
48+
cache.put(i, max);
49+
return max;
6950
}
7051
}
52+
53+
// Time Complexity: Without caching, the recursive part is O(2^n) since there are `n` boxes,
54+
// and 2 options for each box (use or don't use). With caching, the recursive part is just O(n).
55+
// The total time complexity is therefore O(n log n) due to sorting.
56+
// Space Complexity: O(n) due to recursion.
57+
58+
// - A Dynamic Programming Iterative solution (using an array instead of a HashMap)
59+
// is also possible. It will have the same Time/Space complexity as the above solution.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package _8_13_Stack_of_Boxes;
22

3-
import java.util.ArrayDeque;
3+
import java.util.*;
44

55
public class Tester {
66
public static void main(String[] args) {
77
System.out.println("*** Test 8.13: Stack of Boxes\n");
8-
ArrayDeque<Box> boxes = new ArrayDeque<>();
8+
List<Box> boxes = new LinkedList<>();
99
boxes.add(new Box(3, 4, 1));
1010
boxes.add(new Box(8, 6, 2));
1111
boxes.add(new Box(4, 8, 3));
1212
test(boxes);
1313
}
1414

15-
private static void test(ArrayDeque<Box> boxes) {
15+
private static void test(List<Box> boxes) {
1616
System.out.println("Original boxes: " + boxes + "\n");
17-
ArrayDeque<Box> stack = StackOfBoxes.buildTallestStack(boxes);
18-
System.out.println("Tallest stack: " + stack);
17+
int height = StackOfBoxes.findMaxHeight(boxes);
18+
System.out.println("Height of tallest stack: " + height);
1919
}
2020
}

0 commit comments

Comments
 (0)