Remove Boxes — Hard Problem & Solution
A row of boxes is given, boxes[i] being the colour of the i-th box.
- Difficulty: Hard
- Topics: Arrays, Dynamic Programming, Memoization
- Asked at: Amazon, Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
A row of boxes is given, boxes[i] being the colour of the i-th box. In each round you remove one maximal group of k >= 1 adjacent boxes of the same colour and score k * k points; the boxes on either side then become adjacent. You keep going until no boxes remain.
Return the maximum total score you can get.
Example 1
Input: boxes = [1,3,2,2,2,3,4,3,1]
Output: 23
Explanation: Remove `[2,2,2]` (9), then the single 4 (1), then `[3,3,3]` (9), then `[1,1]` (4).
Example 2
Input: boxes = [1,1,1]
Output: 9
Example 3
Input: boxes = [5]
Output: 1
Constraints
1 <= boxes.length <= 1001 <= boxes[i] <= 100
How to solve Remove Boxes
A three-dimensional interval DP. The extra index k records how many boxes of the left end's colour have been saved up to be removed together with it, which is exactly the information plain dp(l, r) loses.
Approach
dp(l, r, k)= maximum points forboxes[l..r]withkextra boxes of colourboxes[l]attached on the left;dp(l, r, k) = 0whenl > r.- Option 1: remove the left group now —
(k + 1)^2 + dp(l + 1, r, 0). - Option 2: for each
minl + 1..rwithboxes[m] == boxes[l], clear the middle first and merge —dp(l + 1, m - 1, 0) + dp(m, r, k + 1). - Take the maximum, memoise it, and return
dp(0, n - 1, 0).
Why it works
Consider the round in which box l disappears. Either it is removed with only its k attached companions (option 1), or it is removed together with some later box of the same colour; letting m be the first such box, everything strictly between them must have been removed before, independently of the rest (option 2). These cases cover every strategy, and each subproblem is again of the same form.
Complexity
- Time —
O(n^4) worst case (n^3 states, O(n) transitions) - Space —
O(n^3)
Pitfalls
- A two-index
dp(l, r)cannot express the merge bonus and gives wrong answers. - The score is
k * kfor the group removed in one round, not one point per box. - Memoise all three indices — without it the recursion is exponential.
Reference solution
Python
import sys
from typing import List
def removeBoxes(boxes: List[int]) -> int:
sys.setrecursionlimit(10000)
n = len(boxes)
memo = {}
def dp(l, r, k):
if l > r:
return 0
key = (l * 128 + r) * 128 + k
if key in memo:
return memo[key]
best = (k + 1) * (k + 1) + dp(l + 1, r, 0)
for m in range(l + 1, r + 1):
if boxes[m] == boxes[l]:
cand = dp(l + 1, m - 1, 0) + dp(m, r, k + 1)
if cand > best:
best = cand
memo[key] = best
return best
return dp(0, n - 1, 0)JavaScript
var removeBoxes = function(boxes) {
var n = boxes.length;
var memo = new Int32Array(n * n * n).fill(-1);
var dp = function(l, r, k) {
if (l > r) return 0;
var key = (l * n + r) * n + k;
if (memo[key] >= 0) return memo[key];
var best = (k + 1) * (k + 1) + dp(l + 1, r, 0);
for (var m = l + 1; m <= r; m++) {
if (boxes[m] === boxes[l]) {
var cand = dp(l + 1, m - 1, 0) + dp(m, r, k + 1);
if (cand > best) best = cand;
}
}
memo[key] = best;
return best;
};
return dp(0, n - 1, 0);
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 988 arrays problems · the whole catalogue
Learn the technique: Arrays · Dynamic Programming