Burst Balloons is the classic hard interval-DP problem. It looks like a greedy puzzle, but the winning move is to flip the question around: instead of asking which balloon to burst first, ask which one to burst last.
Problem. You have n balloons, each painted with a number in nums. Bursting balloon i earns
nums[i-1] * nums[i] * nums[i+1] coins, where out-of-range neighbors count as 1. After a balloon
bursts, its neighbors become adjacent. Return the maximum coins you can collect by bursting every
balloon.
Example: nums = [3, 1, 5, 8] → answer 167.
The slow way first
The tempting move is to try bursting balloons one at a time and recurse on what is left. But when you burst a balloon "first," its two neighbors merge — so the score of every remaining balloon now depends on the order of past bursts. The subproblems overlap in a tangled way and there is no clean state to memoize. Brute-forcing all orders is O(n!) — hopeless.
The question to ask: what choice leaves two clean, independent subproblems? Bursting first does not. Bursting last does.
The idea: pick the last balloon to burst
First pad the array with a 1 on each end: pad = [1] + nums + [1]. Now consider an interval (l, r) of balloons. Suppose balloon k is the last one we burst inside it. By the time it pops, everything else in (l, r) is gone, so its only neighbors are the fixed boundaries pad[l-1] and pad[r+1]. That burst earns pad[l-1] * pad[k] * pad[r+1], and the two sides (l, k-1) and (k+1, r) are now independent subproblems.
So dp[l][r] = max over k of pad[l-1]*pad[k]*pad[r+1] + dp[l][k-1] + dp[k+1][r]. We fill the table by interval length so every smaller interval is ready before the one that needs it.
Walk through it
Step through the animation. The dp triangle fills diagonal by diagonal: length-1 intervals first (a single balloon between its two boundaries), then length 2, 3, and finally the full range (1, 4). Each cell tries every k as the last burst and keeps the max. The top-right cell dp[1][4] is the final answer: 167.
Pseudocode
pad = [1] + nums + [1]
dp = (n x n) table of zeros
for length from 1 to number of balloons:
for each interval (l, r) of that length:
for each balloon k in l..r:
gain = pad[l-1] * pad[k] * pad[r+1] # k is burst LAST
total = gain + dp[l][k-1] + dp[k+1][r]
dp[l][r] = max(dp[l][r], total)
return dp[1][n-2] # whole padded rangeThe Python solution
def max_coins(nums):
pad = [1] + nums + [1]
n = len(pad)
dp = [[0] * n for _ in range(n)]
for length in range(1, n - 1):
for l in range(1, n - length):
r = l + length - 1
for k in range(l, r + 1):
gain = pad[l - 1] * pad[k] * pad[r + 1]
total = gain + dp[l][k - 1] + dp[k + 1][r]
dp[l][r] = max(dp[l][r], total)
return dp[1][n - 2]padadds the imaginary1boundaries so we never index out of bounds.- The outer
lengthloop guarantees bothdp[l][k-1]anddp[k+1][r]are already computed. gainis the coins from burstingklast, when its neighbors are exactly the fixed boundaries.- Lines 9-11 are the heart: try each
kas the last burst and keep the best total. dp[1][n-2]covers the original (unpadded) array — the answer.
Complexity
| Case | Time | Notes |
|---|---|---|
| Try every burst order | O(n!) (slow) | brute force, hopeless |
| Interval DP (this solution) | O(n³) (moderate) | n² intervals x n choices of k |
O(n²) (slow)The O(n²) table holds one entry per interval, and filling each entry scans up to n candidates for k, giving O(n³).
When this pattern shows up
When a problem asks for the best way to combine or remove items over a range and a sub-choice merges
the neighbors, think interval DP: define dp[l][r] over a range and pick the element handled
last (or a split point) so the two sides become independent. Matrix-chain multiplication, optimal
BST, and "remove boxes" all use this shape.
The trick is choosing k as the last balloon, not the first. If you pick it first, the neighbors
keep changing as the sides are cleared and the subproblems are no longer independent.
Practice
For pad = [1, 3, 1, 5, 8, 1], what does dp[1][1] (burst balloon 3 alone, value 3) earn, and why?
1. Why do we choose k as the LAST balloon burst in an interval instead of the first?
2. Why is the array padded with a 1 on each end?
3. Why must we fill the dp table by increasing interval length?
4. What is the time complexity of this interval-DP solution?