|
| 1 | +--- |
| 2 | +title: "546. Remove Boxes" |
| 3 | +date: "2025-10-08" |
| 4 | +tags: ["Dynamic Programming", "Interval DP"] |
| 5 | +related: [] |
| 6 | +slug: "546-remove-boxes" |
| 7 | +category: "leetcode" |
| 8 | +leetcode_url: "https://leetcode.com/problems/remove-boxes/description/?envType=problem-list-v2&envId=xxzz5vc6" |
| 9 | +leetcode_difficulty: "Hard" |
| 10 | +leetcode_topics: ["Dynamic Programming", "Interval DP"] |
| 11 | +--- |
| 12 | + |
| 13 | +# 546. Remove Boxes |
| 14 | + |
| 15 | +## Summary |
| 16 | +Given an array of box colors, maximize points by removing consecutive boxes of the same color, where each removal of k boxes gives k² points, using interval DP with state representing subarrays and trailing box counts. |
| 17 | + |
| 18 | +## Problem Description |
| 19 | +You are given a list of integers `boxes` where each integer represents the color of a box. You can perform the following operation any number of times: |
| 20 | + |
| 21 | +- Select a contiguous group of boxes of the same color and remove them. |
| 22 | +- For each removal, you get `k²` points where `k` is the number of boxes you removed. |
| 23 | + |
| 24 | +Return the maximum number of points you can get. |
| 25 | + |
| 26 | +**Constraints:** |
| 27 | +- `1 <= boxes.length <= 100` |
| 28 | +- `1 <= boxes[i] <= 100` |
| 29 | + |
| 30 | +## Solution Approach |
| 31 | +This is a complex **interval dynamic programming** problem that requires careful state definition. |
| 32 | + |
| 33 | +### Key Insight |
| 34 | +The key insight is that when we have a subarray `boxes[i..j]`, and we want to remove boxes of color `boxes[j]`, we can potentially merge with other boxes of the same color that appear later in the array. |
| 35 | + |
| 36 | +### DP State Definition |
| 37 | +`dp[i][j][k]` represents the maximum points we can get from subarray `boxes[i..j]` where there are `k` boxes of color `boxes[j]` that we can merge with (these would be boxes that appear after position `j` but have the same color). |
| 38 | + |
| 39 | +### DP Transition |
| 40 | +For each `dp[i][j][k]`: |
| 41 | +1. **Option 1**: Remove the last box `boxes[j]` alone (if k > 0, we get 1² points, but we can also merge with the k trailing boxes) |
| 42 | +2. **Option 2**: Find the last position `m` (m < j) where `boxes[m] == boxes[j]`, and combine the removal of boxes[m+1..j] with the trailing boxes |
| 43 | + |
| 44 | +### Base Cases |
| 45 | +- `dp[i][i][k] = (k + 1)²` for any k (we can remove the single box, plus k trailing boxes of same color) |
| 46 | + |
| 47 | +## Time & Space Complexity |
| 48 | +- **Time Complexity:** O(n³) where n is length of boxes (n ≤ 100) |
| 49 | +- **Space Complexity:** O(n³) for the 3D DP table |
| 50 | + |
| 51 | +## Key Insights |
| 52 | +- **State compression**: We need 3 dimensions because trailing boxes of the same color as the end can be merged |
| 53 | +- **Interval merging**: We can merge intervals when we find boxes of the same color |
| 54 | +- **Greedy merging**: Always try to merge consecutive same-color boxes for maximum points |
| 55 | +- **Recursive structure**: The DP naturally captures the recursive nature of choosing which boxes to remove first |
| 56 | + |
| 57 | +## Examples / snippets |
| 58 | + |
| 59 | +### Solution Code (Interval DP) |
| 60 | +```python |
| 61 | +from typing import List |
| 62 | + |
| 63 | +class Solution: |
| 64 | + def removeBoxes(self, boxes: List[int]) -> int: |
| 65 | + n = len(boxes) |
| 66 | + # dp[i][j][k]: max points for subarray i..j with k trailing boxes of color boxes[j] |
| 67 | + dp = [[[0] * n for _ in range(n)] for _ in range(n)] |
| 68 | + |
| 69 | + def solve(i, j, k): |
| 70 | + if i > j: |
| 71 | + return 0 |
| 72 | + if dp[i][j][k] > 0: |
| 73 | + return dp[i][j][k] |
| 74 | + |
| 75 | + # Remove trailing boxes of same color first |
| 76 | + while j > i and boxes[j] == boxes[j-1]: |
| 77 | + j -= 1 |
| 78 | + k += 1 |
| 79 | + |
| 80 | + # Remove the group of boxes[j] with k+1 boxes total |
| 81 | + dp[i][j][k] = (k + 1) * (k + 1) + solve(i, j-1, 0) |
| 82 | + |
| 83 | + # Try to merge with earlier boxes of same color |
| 84 | + for m in range(i, j): |
| 85 | + if boxes[m] == boxes[j]: |
| 86 | + # Remove boxes between m and j first, then combine with trailing boxes |
| 87 | + dp[i][j][k] = max(dp[i][j][k], |
| 88 | + solve(i, m, k+1) + solve(m+1, j-1, 0)) |
| 89 | + |
| 90 | + return dp[i][j][k] |
| 91 | + |
| 92 | + return solve(0, n-1, 0) |
| 93 | +``` |
| 94 | + |
| 95 | +### Solution Code (Bottom-up DP) |
| 96 | +```python |
| 97 | +from typing import List |
| 98 | + |
| 99 | +class Solution: |
| 100 | + def removeBoxes(self, boxes: List[int]) -> int: |
| 101 | + n = len(boxes) |
| 102 | + # dp[i][j][k]: max points for subarray i..j with k trailing boxes of color boxes[j] |
| 103 | + dp = [[[0] * (n + 1) for _ in range(n)] for _ in range(n)] |
| 104 | + |
| 105 | + # Fill DP table in order of increasing length |
| 106 | + for length in range(1, n + 1): |
| 107 | + for i in range(n - length + 1): |
| 108 | + j = i + length - 1 |
| 109 | + |
| 110 | + # For each possible k (trailing boxes) |
| 111 | + for k in range(n - j): |
| 112 | + # Remove trailing boxes of same color first |
| 113 | + while j > i and boxes[j] == boxes[j-1]: |
| 114 | + j -= 1 |
| 115 | + k += 1 |
| 116 | + |
| 117 | + # Remove the group of boxes[j] with k+1 boxes total |
| 118 | + dp[i][j][k] = (k + 1) * (k + 1) |
| 119 | + |
| 120 | + # Add points from removing boxes[i..j-1] |
| 121 | + if i <= j - 1: |
| 122 | + dp[i][j][k] += dp[i][j-1][0] |
| 123 | + |
| 124 | + # Try to merge with earlier boxes of same color |
| 125 | + for m in range(i, j): |
| 126 | + if boxes[m] == boxes[j]: |
| 127 | + # Remove boxes between m and j first, then combine with trailing boxes |
| 128 | + if m + 1 <= j - 1: |
| 129 | + dp[i][j][k] = max(dp[i][j][k], |
| 130 | + dp[i][m][k+1] + dp[m+1][j-1][0]) |
| 131 | + else: |
| 132 | + dp[i][j][k] = max(dp[i][j][k], dp[i][m][k+1]) |
| 133 | + |
| 134 | + return dp[0][n-1][0] |
| 135 | +``` |
| 136 | + |
| 137 | +### Example Walkthrough |
| 138 | +**Example 1:** |
| 139 | +``` |
| 140 | +Input: boxes = [1,3,2,2,2,3,4,3,1] |
| 141 | +Output: 23 |
| 142 | +
|
| 143 | +Let's trace through the DP: |
| 144 | +- We need to consider subarrays and how to merge same-color boxes |
| 145 | +- The optimal strategy involves removing middle boxes to combine same colors |
| 146 | +- Final answer is 23 points ✓ |
| 147 | +``` |
| 148 | + |
| 149 | +**Example 2:** |
| 150 | +``` |
| 151 | +Input: boxes = [1,2,2,1,1] |
| 152 | +Output: 9 |
| 153 | +
|
| 154 | +Step by step: |
| 155 | +- Remove two 1's at the end: 2² = 4 points, left with [1,2,2] |
| 156 | +- Remove two 2's: 2² = 4 points, left with [1] |
| 157 | +- Remove last 1: 1² = 1 point |
| 158 | +- Total: 4 + 4 + 1 = 9 ✓ |
| 159 | +``` |
| 160 | + |
| 161 | +## Edge Cases & Validation |
| 162 | +- **Single box**: `[5]` should return 1 (1²) |
| 163 | +- **All same color**: `[2,2,2,2]` should return 16 (4²) |
| 164 | +- **No same colors**: `[1,2,3,4]` should return 4 (four 1² removals) |
| 165 | +- **Alternating colors**: `[1,2,1,2]` should return 4 (four 1² removals) |
| 166 | +- **Maximum merging**: `[3,3,3]` should return 9 (3²) |
| 167 | + |
| 168 | +## Related Problems |
| 169 | +- [312. Burst Balloons](https://leetcode.com/problems/burst-balloons/) - Similar interval DP with different scoring |
| 170 | +- [375. Guess Number Higher or Lower II](https://leetcode.com/problems/guess-number-higher-or-lower-ii/) - Game theory with interval DP |
| 171 | +- [546. Remove Boxes](https://leetcode.com/problems/remove-boxes/) - This problem |
| 172 | +- [1039. Minimum Score Triangulation of Polygon](https://leetcode.com/problems/minimum-score-triangulation-of-polygon/) - Another interval DP problem |
| 173 | +``` |
0 commit comments