Taro Logo

Remove Boxes

Medium
1 views
2 months ago

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. Return the maximum points you can get.

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] (11=1 points) ----> [1, 1] (33=9 points) ----> [] (22=4 points)

Example 2:

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

Example 3:

Input: boxes = [1] Output: 1

Constraints: 1 <= boxes.length <= 100 1 <= boxes[i] <= 100

Sample Answer
class Solution:
    def removeBoxes(self, boxes: list[int]) -> int:
        n = len(boxes)
        dp = {}  # Memoization: (l, r, k) -> max_points

        def calculate_points(l: int, r: int, k: int) -> int:
            if l > r:
                return 0
            if (l, r, k) in dp:
                return dp[(l, r, k)]

            # Optimization: Extend the existing consecutive boxes
            while l < r and boxes[l] == boxes[l + 1]:
                l += 1
                k += 1

            max_points = (k + 1) ** 2 + calculate_points(l + 1, r, 0)

            # Explore other options: Find a box with the same color and merge
            for m in range(l + 1, r + 1):
                if boxes[l] == boxes[m]:
                    max_points = max(max_points,
                                     calculate_points(l + 1, m - 1, 0) +
                                     calculate_points(m, r, k + 1))

            dp[(l, r, k)] = max_points
            return max_points

        return calculate_points(0, n - 1, 0)

Explanation:

  1. Problem Understanding:

    • The problem asks to find the maximum points obtainable by removing continuous boxes of the same color. Removing k boxes yields k*k points.
  2. Approach:

    • Dynamic Programming with Memoization.
    • The core idea is to consider different combinations of removing boxes and memoize the results to avoid recomputation.
  3. Code Breakdown:

    • removeBoxes(boxes: List[int]) -> int
      • This is the main function that takes the input list of boxes and returns the maximum points.
    • calculate_points(l: int, r: int, k: int) -> int
      • This is a recursive function with memoization that calculates the maximum points obtainable from the sub-array boxes[l:r+1], given that there are k boxes of the same color as boxes[l] already removed from the left.
      • Base case: if l > r: return 0. If the left index crosses the right index, no boxes are left, so the points are 0.
      • Memoization: if (l, r, k) in dp: return dp[(l, r, k)]. Checks if the result has already been calculated and stored in dp.
      • Optimization: Extends consecutive same-color boxes from the left to maximize the number of boxes to remove together and get higher points.
      • Main Logic:
        • Removes the k+1 boxes starting from index l: (k + 1) ** 2 + calculate_points(l + 1, r, 0).
        • The algorithm iterates through the rest of the boxes to find if there is a box with same color, and then merges them to remove.
      • Memoize the results: dp[(l, r, k)] = max_points

Example Walkthrough:

Let's walk through the example boxes = [1, 3, 2, 2, 2, 3, 4, 3, 1]

The function calculate_points(0, 8, 0) will be called initially.

  • It tries removing just boxes[0] (which is 1). But before that, the while loop does nothing because l < r and boxes[l] == boxes[l+1] is false. Then it finds other indexes with same color = 1.
  • In the for loop, m becomes 8.
  • Now it calculates calculate_points(1, 7, 0) + calculate_points(8, 8, 1).
  • calculate_points(1, 7, 0) recurses and tries to remove 3, etc.
  • The dp memoizes the results. The rest of the logic is finding optimal splitting points and calculating the maximum number of points.

Big O Run-time Analysis:

  • Time Complexity: O(n^4), where n is the number of boxes. The calculate_points function has three parameters (l, r, k), each ranging from 0 to n. The for loop inside calculate_points adds another factor of n. Memoization reduces the number of actual computations, but in the worst case, it explores all possible combinations.

Big O Space Usage Analysis:

  • Space Complexity: O(n^3). The memoization table dp stores results for different combinations of (l, r, k), each ranging from 0 to n. Thus the space used by the memoization table is O(n^3).

Edge Cases:

  • Empty Input:
    • If the input boxes is empty, the code handles it gracefully because n = len(boxes) will be 0, and the initial call to calculate_points(0, n - 1, 0) will immediately return 0 because l > r.
  • Single Box:
    • If the input contains only one box (e.g., boxes = [1]), the function will correctly return 1. calculate_points(0, 0, 0) will return (0 + 1) ** 2 + calculate_points(1, 0, 0) and calculate_points(1, 0, 0) will be zero.
  • All Boxes Same Color:
    • If all boxes have the same color (e.g., boxes = [1, 1, 1]), the optimization while loop will extend the number of boxes considered together, resulting in the correct output. For [1, 1, 1], calculate_points(0, 2, 0) will result in 9.
  • Large Input:
    • The code should work for inputs up to the specified constraint (1 <= boxes.length <= 100). The memoization ensures that computations are not repeated for the same subproblems, making it feasible for reasonably sized inputs.