The problem
A wooden stick has length n, with positions 0 to n. You must make every cut listed in cuts, in any order you choose. Each cut costs the length of the piece being cut at that moment. Return the minimum total cost.
Input: n = 7, cuts = [1, 3, 4, 5]
Output: 16Cut at 3 first (cost 7). Then cut the left piece [0, 3] at 1 (cost 3), and the right piece [3, 7] at 5 (cost 4) and at 4 (cost 2). Total 7 + 3 + 4 + 2 = 16.
How to think about it
Start with brute force. Try every order of the m cuts: m! orders. With 100 cuts, impossible.
The key insight: the first cut splits the problem into two independent ones. Once you cut a piece at position c, the left and right halves never interact again. So the cost of finishing a piece is: its own length (for whichever cut you make first in it) plus the best cost of each half.
Index by cuts, not by length. n can be a million, so don’t build a table over positions. Sort the cuts and add the ends: cuts = [0, 1, 3, 4, 5, 7]. A piece is now a pair of indices (l, r) into this list, and the cuts strictly between them are the ones it still needs. Then:
dp[l][r] = min over l < k < r of dp[l][k] + dp[k][r] + (cuts[r] − cuts[l])
A piece with no cuts inside (r = l + 1) costs 0.
Which pattern is this? Interval DP, the same shape as Burst Balloons or Matrix Chain Multiplication. The tell is “the order of operations on a range changes the cost”: pick one operation that splits the range, and recurse on both sides.
Approach
- Sort
[0] + cuts + [n]intocuts, withm = len(cuts). - Create an
m × mtable of zeros. - For
lengthfrom 2 tom - 1, and eachl, letr = l + length. - Set
dp[l][r]to the min over every middlekofdp[l][k] + dp[k][r], plus the piece lengthcuts[r] - cuts[l]. - Return
dp[0][m - 1].
Solution
def minCost(n, cuts):
cuts = sorted([0] + cuts + [n]); m = len(cuts) # add the stick's ends
dp = [[0] * m for _ in range(m)]
for length in range(2, m): # shorter pieces first
for l in range(m - length):
r = l + length
# cut first at k: pay this piece's length, then solve both halves
dp[l][r] = min(dp[l][k] + dp[k][r] for k in range(l + 1, r)) + cuts[r] - cuts[l]
return dp[0][m - 1]
# minCost(7, [1,3,4,5]) -> 16Dry run
cuts = [0, 1, 3, 4, 5, 7]. Each row is a piece by its end positions, the cut chosen first in it, and the cost.
| Piece | Piece length | Best first cut | dp |
|---|---|---|---|
| [0, 3] | 3 | 1 | 3 |
| [3, 5] | 2 | 4 | 2 |
| [4, 7] | 3 | 5 | 3 |
| [3, 7] | 4 | 5 | 6 |
| [0, 5] | 5 | 3 | 10 |
| [1, 7] | 6 | 3 | 12 |
| [0, 7] | 7 | 3 | 16 |
The whole stick: cut at 3 first, dp[0,3] + dp[3,7] + 7 = 3 + 6 + 7 = 16.
Complexity
- Time: O(m³), where
mis the number of cuts plus 2. There are O(m²) pieces and each tries O(m) first cuts. It does not depend onn. - Space: O(m²). For the table.
Edge cases and pitfalls
- One cut costs exactly
n. - Forgetting to sort the cuts breaks the “cuts strictly between
landr” meaning. - A table over positions
0..nis O(n²) memory, which fails whennis large. Index over cuts instead. - Greedy “cut nearest the middle first” is not always optimal; you need the DP.
- Missing the piece length. The
+ cuts[r] - cuts[l]belongs outside themin, once per piece.
Related problems
- Burst Balloons (LeetCode 312): the same interval DP, thinking about the last action instead of the first.
- Stone Game (LeetCode 877): interval DP on a shrinking range.
- Partition Array for Maximum Sum (LeetCode 1043): splitting an array, but with independent blocks so 1-D DP is enough.
- 1.Partition to K Equal Sum Subsets (LeetCode 698)
- 2.House Robber III (LeetCode 337)
- 3.Stone Game (LeetCode 877)
- 4.Partition Array for Maximum Sum (LeetCode 1043)
- 5.Shortest Path Visiting All Nodes (LeetCode 847)
- 6.Burst Balloons (LeetCode 312)
- 7.Minimum Cost to Cut a Stick (LeetCode 1547)
- 8.Count Vowels Permutation (LeetCode 1220)
- 9.Student Attendance Record II (LeetCode 552)
- 10.Maximum Profit in Job Scheduling (LeetCode 1235)