Remove Boxes

Solution For Remove Boxes

Problem Statement:

You are given several boxes with different colors represented by different positive numbers.
You may experience several rounds to remove boxes until there is no box left. Each time you can choose some continuous boxes with the same color (i.e., composed of k boxes, k >= 1), remove them and get k * k points.
Find the maximum points you can get.

Solution Approach:

This problem belongs to dynamic programming and to understand the nature of the problem clearly, let us consider a few test cases:
Example 1:

Input: boxes = [1,3,2,2,2,3,4,3,1] Output: 23
Explanation:
[1, 3, 2, 2, 2, 3, 4, 3, 1] —-> [1, 3, 3, 4, 3, 1] (33=9 points)
—-> [1, 3, 3, 3, 1] (1
1=1 points)
—-> [1, 1] (33=9 points)
—-> [] (2
2=4 points)
Thus, totally 9 + 1 + 9 + 4 = 23 points

Example 2:

Input: boxes = [1,1,1] Output: 9

Input: boxes = [1,1] Output: 4
Example 5:

Input: boxes = [1] Output: 1
From the above examples, we can conclude that the order of removing the boxes is important.

Hence, our goal here is to create a recursive scheme that will remove the boxes in such a way as to maximize the points earned. As we develop the recursive algorithm, we will note that the number of boxes is important so we will include it as a variable i. Since we aim to maximize the number of points, we will keep track of the points earned parameterized by i in order to return the maximum possible points.

The first step is to define the base cases. Here, the base cases should occur when there are less than two boxes and should only return i*i.

Next, for each box in the input array, we will consider consecutive segments with the same color. When we find a segment, we will provide that segment as input to the recursive definition while keeping track of the maximum points earned. We will then return the maximum points earned.

Now, if we have processed the entire array, the maximum points would have been obtained. We will return the maximum points.

Code:
Let us now implement the above approach through code.

    def removeBoxes(self, boxes: List[int]) -> int:

        memo = {}

        def dp(i, j, k):

            if i > j: return 0

            if (i, j, k) in memo:
                return memo[(i, j, k)]

            # If we have consecutive boxes of the same color, increase k
            nonlocal boxes
            while i < j and boxes[j] == boxes[j-1]:
                j -= 1
                k += 1

            # Case 1: Remove box[j] separately before the last box so that the last box can be matched with adjacent boxes
            ans = dp(i, j-1, 0) + (k+1)*(k+1)

            # Case 2: Match box[j] with an adjacent box that is not the last box
            for m in range(i, j):
                if boxes[m] == boxes[j]:
                    ans = max(ans, dp(i, m, k+1) + dp(m+1, j-1, 0))

            memo[(i, j, k)] = ans

            return ans

        return dp(0, len(boxes)-1, 0)

Step by Step Implementation For Remove Boxes

/**
 * Given several boxes with different colors represented by different positive numbers. 
 * You may experience several rounds to remove boxes until there is no box left. Each time you can choose some continuous boxes with the same color (composed of k boxes, k >= 1), remove them and get k*k points.
 * Find the maximum points you can get.
 * 
 * Input: boxes = [1,3,2,2,2,3,4,3,1]
 * Output: 23
 * Explanation:
 * [1, 3, 2, 2, 2, 3, 4, 3, 1] 
 * ----> [1, 3, 3, 4, 3, 1] (3*3=9 points) 
 * ----> [1, 3, 3, 3, 1] (1*1=1 points) 
 * ----> [1, 1] (3*3=9 points) 
 * ----> [] (2*2=4 points)
 */
 
public class Solution {
    public int removeBoxes(int[] boxes) {
        int n = boxes.length;
        int[][][] dp = new int[n][n][n];
        return removeBoxesSub(boxes, 0, n - 1, 0, dp);
    }
    
    private int removeBoxesSub(int[] boxes, int i, int j, int k, int[][][] dp) {
        if (i > j) return 0;
        if (dp[i][j][k] != 0) return dp[i][j][k];
        
        for (; i + 1 <= j && boxes[i + 1] == boxes[i]; i++, k++); // optimization: skip all equal elements from left
        
        int res = (k + 1) * (k + 1) + removeBoxesSub(boxes, i + 1, j, 0, dp); //remove all equal elements from left and start from 0
        
        for (int m = i + 1; m <= j; m++) {
            if (boxes[m] == boxes[i]) {
                res = Math.max(res, removeBoxesSub(boxes, i + 1, m - 1, 0, dp) + removeBoxesSub(boxes, m, j, k + 1, dp));
            }
        }
        
        dp[i][j][k] = res;
        return res;
    }
}
class Solution:
    def removeBoxes(self, boxes: List[int]) -> int:
        # DP problem - we want to find the maximum score we can get by removing boxes
        # The score for a certain configuration is the number of boxes we can remove 
        # plus the maximum score we can get by removing boxes after the current configuration
        
        # We can use a 2D array to keep track of the maximum score for each configuration
        # The first dimension represents the starting index of the boxes we are considering
        # The second dimension represents the ending index of the boxes we are considering
        # The value at dp[i][j] is the maximum score we can get by considering boxes[i] to boxes[j]
        
        # We can fill in the array by considering different subproblems
        # For each subproblem, we can either remove the first box or not remove the first box
        # If we remove the first box, we update the score and move on to the next box
        # If we don't remove the first box, we need to find the next box that has the same value 
        # as the first box and remove all boxes in between
        
        # We can use a helper function to find the next box with the same value
        # The helper function takes in the current index, the list of boxes, and the value we are looking for
        # It returns the index of the next box with the same value
        
        # Base case - if i > j, then there are no boxes left to consider
        # In this case, the maximum score is 0
        
        # Recursive case - if i <= j, we consider two different cases
        # Case 1 - we remove the first box
        # In this case, the maximum score is the value of the box plus the maximum score we can get by considering boxes[i+1] to boxes[j]
        
        # Case 2 - we don't remove the first box
        # In this case, we need to find the next box with the same value as the first box
        # We remove all boxes in between the first box and the next box
        # The maximum score is the number of boxes we removed plus the maximum score we can get by considering boxes[i+1] to boxes[next]
        
        # We compare Case 1 and Case 2 to find the maximum score and return that value
        
        # Fill in the 2D array with -1 to represent that we haven't considered that configuration yet
        dp = [[-1 for j in range(len(boxes))] for i in range(len(boxes))]
        
        # Helper function to find the next box with the same value
        def find_next(i, boxes, val):
            while i < len(boxes) and boxes[i] != val:
                i += 1
            return i
        
        # Function to find the maximum score we can get by considering boxes[i] to boxes[j]
        def get_score(i, j):
            # Base case - if i > j, then there are no boxes left to consider
            if i > j:
                return 0
            
            # Check if we have already considered this configuration
            if dp[i][j] != -1:
                return dp[i][j]
            
            # Recursive case - we consider two different cases
            
            # Case 1 - we remove the first box
            # In this case, the maximum score is the value of the box plus the maximum score we can get by considering boxes[i+1] to boxes[j]
            score = boxes[i] + get_score(i+1, j)
            
            # Case 2 - we don't remove the first box
            # In this case, we need to find the next box with the same value as the first box
            # We remove all boxes in between the first box and the next box
            # The maximum score is the number of boxes we removed plus the maximum score we can get by considering boxes[i+1] to boxes[next]
            next = find_next(i+1, boxes, boxes[i])
            while next <= j:
                # Remove all boxes between i and next
                score = max(score, get_score(i+1, next-1) + get_score(next, j))
                # Find the next box with the same value
                next = find_next(next+1, boxes, boxes[i])
            
            # Save the maximum score for this configuration and return it
            dp[i][j] = score
            return score
        
        # Get the maximum score for the entire list of boxes
        return get_score(0, len(boxes)-1)
/**
 * @param {number[]} boxes
 * @return {number}
 */
var removeBoxes = function(boxes) {
    // create a 2D array of dp values, where dp[i][j] represents 
    // the maximum point value that can be achieved by removing 
    // boxes from index i to index j
    const dp = [];
    for (let i = 0; i < boxes.length; i++) {
        dp.push(new Array(boxes.length).fill(0));
    }
    
    // iterate through dp values from left to right and top to bottom
    // note: we only need to consider the top-left half of the dp array
    // since the rest of the values will be filled in as we iterate
    for (let i = 0; i < boxes.length; i++) {
        for (let j = i; j < boxes.length; j++) {
            // if i === j, that means there is only 1 box at this index
            // so the point value is 1
            if (i === j) {
                dp[i][j] = 1;
            } else {
                // otherwise, we need to compare the box at index i with 
                // the next box at index j. if they are the same value, 
                // we can remove them both and add 2 to the point value
                // note: we also need to consider the point value we can get 
                // from removing any boxes between i and j
                if (boxes[i] === boxes[j]) {
                    dp[i][j] = dp[i+1][j-1] + 2;
                } else {
                    // if the boxes at index i and j are not the same, 
                    // we will get the maximum point value by removing 
                    // the box at index i and comparing it with the rest 
                    // of the boxes at index j or higher
                    dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
                }
            }
        }
    }
    
    // return the bottom-right value in the dp array
    return dp[0][boxes.length - 1];
};
The problem is as follows:

Given boxes = [1, 3, 2, 2, 2, 3, 4, 3, 1],

Your task is to remove all boxes in one pass using the following algorithm:

Start with an empty list of boxes.
For each box b in boxes:
If b is not empty:
Append b to the list.
If the list is non-empty and its last element is equal to b:
Remove the last element from the list and append it to the result.
Continue until boxes is empty.
Return the result.

#include 
#include 
#include 

using namespace std;

class Solution {
public:
    int removeBoxes(vector& boxes) {
        int n = boxes.size();
        vector>> dp(n, vector>(n, vector(n, 0)));
        return removeBoxesSub(boxes, 0, n - 1, 0, dp);
    }

private:
    int removeBoxesSub(vector& boxes, int i, int j, int k, vector>>& dp) {
        if (i > j) return 0;
        if (dp[i][j][k] > 0) return dp[i][j][k];

        dp[i][j][k] = removeBoxesSub(boxes, i, j - 1, 0, dp) + (k + 1) * (k + 1);
        for (int m = i; m < j; ++m) {
            if (boxes[m] == boxes[j]) {
                dp[i][j][k] = max(dp[i][j][k], removeBoxesSub(boxes, i, m, k + 1, dp) + removeBoxesSub(boxes, m + 1, j - 1, 0, dp));
            }
        }
        return dp[i][j][k];
    }
};
public class Solution {
    public int RemoveBoxes(int[] boxes) {
        // TODO: Implement RemoveBoxes
    }
}


Top 100 Leetcode Practice Problems In Java

Get 30% Off Instantly!
[gravityforms id="5" description="false" titla="false" ajax="true"]