Maximum Segments
Given a length n and three allowed segment lengths, split n into the maximum possible number of segments using only those three lengths, reusing any length as often as needed. Report -1 if no such split exists.
Do this lesson first: coin changeExample input
n = 9, lengths = {3, 5, 7}
Expected output
3
Break it down
Answer each question out loud before you open it. Getting it wrong here is the useful part. A revealed answer you never guessed at teaches you nothing.
Fill the table
The table pauses before each cell you have to supply. Type the value the recurrence gives, and the write animation confirms it.
Press start. The animation stops at every cell YOUR recurrence must fill.
dp[0] = 0 is the only base: an empty remainder needs no segments at all, and it is a genuine answer, not a placeholder. Every cell from i = 1 up depends only on smaller indices (i - a, i - b, i - c, whichever are non-negative), so filling left to right guarantees each candidate already holds a real value, whether that value is a segment count or the impossible marker, before it is read. Lengths smaller than every allowed length (i = 1, i = 2 here) have no candidate at all: no length fits inside them, so they are impossible before the recurrence even runs.
- 1
function maxSegments(n, lengths) { - 2
const dp = new Array(n + 1).fill(-Infinity); - 3
dp[0] = 0; - 4
for (let i = 1; i <= n; i++) { - 5
for (const len of lengths) { - 6
if (i - len >= 0 && dp[i - len] + 1 > dp[i]) dp[i] = dp[i - len] + 1; - 7
} - 8
} - 9
return dp[n]; - 10
}
The code, the trap, the variations
- 1
function maxSegments(n, lengths) { - 2
const dp = new Array(n + 1).fill(-Infinity); - 3
dp[0] = 0; - 4
for (let i = 1; i <= n; i++) { - 5
for (const len of lengths) { - 6
if (i - len >= 0 && dp[i - len] + 1 > dp[i]) dp[i] = dp[i - len] + 1; - 7
} - 8
} - 9
return dp[n]; - 10
}
Where people go wrong
Treating an unreachable length as zero segments instead of impossible. Lengths 1, 2, and 4 cannot be built from 3, 5, and 7 at all, so dp[1], dp[2], and dp[4] are not zero, they are undefined states that must propagate as impossible so no later cell mistakes them for a valid, empty split. Folding an impossible predecessor to 0 would let a later cell wrongly count a phantom extra segment through a remainder that was never actually reachable.
Use segment lengths 2, 3, and 5 instead of 3, 5, and 7.
Almost every length becomes reachable once n grows past a couple of small cases, so the impossible cells mostly disappear. The recurrence itself, dp[i] = 1 + max over reachable predecessors, does not change at all.
Ask for the minimum number of segments instead of the maximum.
combine switches from max to min over the same three candidates. The unreachable-state handling is untouched, since a length that cannot be split at all is still impossible no matter which extreme you optimize for.