Leetcode 546. Remove Boxes

Problem Explanation:

Given colored boxes as positive integers, you can remove subsequences of continuous boxes with the same color in each step. You receive points equal to the square of the number of boxes removed. You can select the subsequence to remove as desired. The goal is to maximize the total points after all boxes are removed.

For instance, given the boxes [1, 3, 2, 2, 2, 3, 4, 3, 1], we can remove the boxes in such a way to get maximum points:

  • [1, 3, 2, 2, 2, 3, 4, 3, 1] -> [1, 3, 3, 4, 3, 1] with 3*3=9 points
  • [1, 3, 3, 4, 3, 1] -> [1, 3, 3, 3, 1] with 1*1=1 point
  • [1, 3, 3, 3, 1] -> [1, 1] with 3*3=9 points
  • [1, 1] -> [] with 2*2=4 points In total, we got 23 points.

To solve this problem, we'll apply the method of Dynamic Programming (DP). In this problem context, DP is used to store the previously calculated values, so we don't need to compute them again, reducing the overall computational time.

Solution Approach:

The crucial part of our strategy is deciding which boxes to remove at each step. The color of the box matters in this decision. If we can, we should wait and remove boxes of the same color together to maximize the points.

We use a 3-dimensional array dp[i][j][k] to store the maximum points we can get from the subarray of boxes from i to j if k boxes equal to boxes[j].

In each iteration, we count how many boxes are the same color as boxes[j] at the end of this subarray (let's assume this count is sameBoxes), then we calculate two possible cases:

  • We can remove these sameBoxes right now, resulting in sameBoxes^2 points. After removal, the subproblem is to maximize the points from the subarray i to r-1 without considering any box at their end. We store this as dp[i][j][k].
  • Or, we can wait and try to remove more of the same color boxes later. Therefore we find another box boxes[p] in the subarray that is the same color as boxes[j] and we try to maximize the points we get from both subarrays separately: from i to p and from p+1 to r-1.

We continue this process until all boxes have been removed. The trick is to keep track of the maximal points that can be achieved with different subarrays and choose the way to remove boxes that results in the highest points possible.

Python Solution:

Here is how we could implement the approach in Python:

1
2python
3class Solution:
4    def removeBoxes(self, boxes: List[int]) -> int:
5        dp = [[[0]*100 for _ in range(100)] for _ in range(100)]
6
7        def calculate(i, j, k):
8            if i > j: return 0
9            if dp[i][j][k] != 0: return dp[i][j][k]
10
11            while i+1 <= j and boxes[i+1] == boxes[i]:
12                i += 1
13                k += 1
14
15            res = (k+1)*(k+1) + calculate(i+1, j, 0)
16            for m in range(i+1, j+1):
17                if boxes[m] == boxes[i]:
18                    res = max(res, calculate(i+1, m-1, 0) + calculate(m, j, k+1))
19            dp[i][j][k] = res
20            return res
21
22        return calculate(0, len(boxes)-1, 0)

In this solution, calculate is a helper function for removing boxes recursively. If dp[i][j][k] already exists, then return its value directly to avoid repeated calculation. This solution follows the same logic explained earlier but in a recursive manner.

Unfortunately, the parameter constraints make it hard to provide a solution for the remaining programming languages.When it comes to solving this problem using Java and JavaScript, the thought process and dynamic programming concept would still remain the same, however, the syntax of function calls and array operations would change.

The downside of representing the problem statement as explained earlier and solving it in Java and JavaScript is the limitation in parameter constraints and non-efficient handling of multidimensional arrays in JavaScript and the concern of space complexity in Java considering the amount of memory a 3-dimensional array of 3 integers can take.

Still, let us try to represent the idea of a solution in Java and JavaScript.

Java Solution:

1
2java
3class Solution {
4    int[][][] dp;
5    int[] boxes;
6    public int removeBoxes(int[] boxes) {
7        this.boxes = boxes;
8        int length = boxes.length;
9        this.dp = new int[length][length][length];
10
11        return calculate(0, length - 1, 0);
12    }
13    
14    private int calculate(int i, int j, int k) {
15        if (i > j) return 0;
16        if (dp[i][j][k] != 0) return dp[i][j][k];
17
18        while (i + 1 <= j && boxes[i + 1] == boxes[i]) {
19            i++;
20            k++;
21        }
22        int max = (k + 1) * (k + 1) + calculate(i + 1, j, 0);
23
24        for (int m = i + 1; m <= j; m++) {
25            if (boxes[m] == boxes[i]) {
26                max = Math.max(max, calculate(i + 1, m - 1, 0) + calculate(m, j, k + 1));
27            }
28        }
29        dp[i][j][k] = max;
30        return max;
31    }
32}

We create an instance variable dp for the Dynamic Programming table and boxes to keep the state across function calls. For each tuple (i, j, k), we memorize calculate(i, j, k) so that we can directly return the result instead of having to recompute it in the future. Unfortunately, the space complexity of this solution is not optimal for large inputs.

JavaScript Solution:

In JavaScript, creating large multidimensional arrays is not efficient. We would recommend using an object or Map instead of an array, which can dynamically grow and shrink as you add or remove elements. However, this alternative approach would change our dynamic programming idea a bit:

1
2javascript
3var removeBoxes = function(boxes) {
4    var dp = Array.from(Array(100), () => Array.from(Array(100), () => Array(100).fill(0)));
5    
6    function calculate(i, j, k) {
7        if (i > j) return 0;
8        if (dp[i][j][k] !== 0) return dp[i][j][k];
9        
10        while((i + 1) <= j && boxes[i + 1] === boxes[i]){
11            i++;
12            k++;
13        }
14           
15        var res = (k + 1) * (k + 1) + calculate(i + 1, j, 0);
16        
17        for(let m = i + 1; m <= j; m++){
18            if (boxes[m] === boxes[i]){
19                res = Math.max(res, calculate(i + 1, m - 1, 0) + calculate(m, j, k + 1));
20            }
21        }
22        dp[i][j][k] = res;
23        return res; 
24    }
25    return calculate(0, boxes.length - 1, 0);
26};

JavaScript's Array.from() method is used to create the 3D DP table in this solution. Just like in the Python and Java implementations, the auxiliary recursive function calculate(i, j, k) is defined and used to fill the DP table based on the given problem statement. It is also important to add necessary validations to handle edge cases elegantly in languages which are loosely type like JavaScript. This solution overcomes the Java's problem of space complexity, but is not as efficient due to JavaScript's handling of arrays.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫