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
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)
Problem Understanding:
k
boxes yields k*k
points.Approach:
Code Breakdown:
removeBoxes(boxes: List[int]) -> int
boxes
and returns the maximum points.calculate_points(l: int, r: int, k: int) -> int
boxes[l:r+1]
, given that there are k
boxes of the same color as boxes[l]
already removed from the left.if l > r: return 0
. If the left index crosses the right index, no boxes are left, so the points are 0.if (l, r, k) in dp: return dp[(l, r, k)]
. Checks if the result has already been calculated and stored in dp
.k+1
boxes starting from index l
: (k + 1) ** 2 + calculate_points(l + 1, r, 0)
.dp[(l, r, k)] = max_points
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.
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.m
becomes 8.calculate_points(1, 7, 0) + calculate_points(8, 8, 1)
.calculate_points(1, 7, 0)
recurses and tries to remove 3, etc.dp
memoizes the results. The rest of the logic is finding optimal splitting points and calculating the maximum number of points.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.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).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
.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.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.