skip to content

What should dp[0] hold in a bottom-up DP table indexed by items considered so far?

level: juniorimportance: must knowfreq 68%

answer

  1. what does the index actually count?
  2. the state before any item is seen
  3. an empty prefix still has an answer
  4. identity of the combining operation
  5. sums seed 0, counting seeds 1

basics

~20 s

dp[0] is the empty-prefix answer: zero items considered, not the first item. Seed it with the identity of whatever the transition combines, such as 0 for a sum, 1 for counting the single empty arrangement, or a large sentinel for a minimisation. The first item's result lands in dp[1].

solid answer

~50 s

The index convention is part of the state definition, so state it before writing the loop. If `dp[i]` means "the best answer over the first `i` items", then `dp[0]` describes the empty prefix, before any element is examined, and its value must be the identity of the operation the transition applies: `0` when accumulating a sum, `1` when counting arrangements (there is exactly one way to choose nothing), a large sentinel such as `+infinity` when minimising so that unreachable states never win a comparison. The first real element then updates `dp[1]`, which keeps the transition uniform: every `dp[i]` reads `dp[i-1]`, including at `i == 1`. Candidates who put the first element's value in `dp[0]` end up patching the loop with a special case for the first iteration, and that patch is where the off-by-one bugs live.

go deeper

for a junior

Be ready to say what your index means before you write the loop, and to point at the cell that represents "nothing chosen yet". Know that counting DPs seed the empty state with one, not zero.

for a middle

Explain why the identity value is what makes the transition uniform, and show that a wrong seed is what forces a special case on the first iteration. Derive the base case from the stated meaning rather than recalling it.

for a senior

Demonstrate that you fix index bugs by rewriting the cell's definition, not by nudging bounds until the sample passes. Be able to switch conventions deliberately, and to explain what each convention does to the final read.

for a principal

Own the convention across a codebase: one stated meaning per table, written down next to it, so that a later reader can change a transition without re-deriving the indexing. Inconsistent index conventions across related solvers are a maintenance tax you should refuse to pay.

## The base case is a definition, not a formality Every bottom-up DP has three parts that must agree: what a cell *means*, how a cell is *computed* from earlier cells, and what the cells that no transition can compute must be *seeded* with. Beginners treat the third part as boilerplate ("fill the table with zeros and start the loop"), which is why so many first drafts are off by one. Start with the meaning. The most common convention over a sequence of `n` items is: > `dp[i]` = the answer for the **first `i` items**, i.e. for the prefix of length `i`. Under that convention `dp[0]` is not "item zero" and not "the first item" — it is the answer for the **empty prefix**. The table has `n + 1` cells, indices `0..n`, and the final answer is `dp[n]`. Item `k` (1-based) is folded in when computing `dp[k]`, so inside the loop the item at position `i` and the cell `dp[i]` line up with no adjustment. ## Seeding the empty prefix: use the identity of the combining operation Ask what the transition does to a value, and seed `dp[0]` with the value that leaves that operation unchanged: | What the DP computes | Transition combines with | `dp[0]` | |---|---|---| | Maximum or total accumulated value | addition | `0` | | Number of ways / arrangements | addition of counts, multiplication of independent choices | `1` (there is exactly one way to do nothing) | | Minimum cost, where some states are impossible | `min` | a sentinel larger than any real cost | | Reachability ("can this total be formed?") | logical `or` | reachable = true for the empty selection | The counting case trips people up: "there are zero items, so there are zero ways" sounds right and is wrong. There is exactly **one** way to build nothing — take nothing — and if you seed `0`, every count downstream multiplies or adds from zero and the whole table collapses to zero. The minimisation case is the mirror image: seeding an *unreachable* cell with `0` makes it look like a free, perfect solution, and the `min` happily propagates that fiction to the answer. ## Why the uniform transition matters A seeded `dp[0]` buys you a loop with no special cases: ``` dp[0] = identity for i in 1..n dp[i] = combine(dp[i-1], item i) answer = dp[n] ``` If instead you seed `dp[0]` with the first item's value, then `dp[1]` must not read `dp[0]` the way every other cell reads its predecessor, so you add a branch for `i == 1`. That branch duplicates the transition logic. When the recurrence later changes — a new term, a new constraint — you must remember to change it in two places, and the second place is the one that gets forgotten. ## Other index conventions, and what they do to the base case The prefix convention is not the only one, and each convention implies its own base case and its own final answer: - **`dp[i]` = the best answer for a solution that *ends exactly at* item `i`.** Now there is no meaningful empty state; you seed `dp[1]` (or every `dp[i]` with the item's own standalone value) and the answer is `max` over all `i`, not `dp[n]`. Reading `dp[n]` here is a classic wrong answer: it reports the best solution ending at the last element rather than the best overall. - **Two sequences, `dp[i][j]` over prefixes of lengths `i` and `j`.** Row `0` and column `0` describe one input being empty; they are seeded with the cost of consuming the other sequence from nothing, which is often a running total rather than a flat zero. - **Suffix states, `dp[i]` = the best answer over items `i..n`.** The base case moves to the far end (`dp[n+1]`) and the loop runs downward. The rule behind all of them is the same: **name the cell's meaning in one sentence, then derive the base case from that sentence rather than from habit.** If you cannot say out loud what `dp[0]` means, the table has no definition yet, and any bug you hit later will be argued about rather than diagnosed. ## In an interview Say the convention before you write the loop — "`dp[i]` is the answer over the first `i` items, so `dp[0]` is the empty prefix and it starts at the identity". It takes five seconds, it makes the base case obviously correct rather than a guess, and it removes the single most common source of arguing about indices for the rest of the session.

  • When would you seed the empty-prefix cell with a large sentinel instead of zero?
    When the DP minimises and some states are genuinely unreachable. A zero there claims a free perfect solution, and `min` propagates that fiction into the answer. Use a sentinel bigger than any achievable cost, and make sure the transition cannot overflow when it adds to that sentinel — clamp, or guard the read, rather than letting a huge value wrap around into an attractive small one.
  • If dp[i] instead means the best solution ending exactly at item i, how do the base case and the final answer change?
    There is no empty state to seed; each `dp[i]` starts from the item's own standalone value and improves on it using earlier cells. The final answer becomes the maximum (or minimum) over all `i`, not `dp[n]` — reading only the last cell reports the best solution that happens to end at the last item, which is a different and usually smaller quantity.
  • How do you seed the boundary of a 2D table over two sequences?
    Row `0` and column `0` mean one sequence is empty. Seed them with the cost of consuming the other sequence from nothing — often a running total, not a flat zero — and seed `dp[0][0]` with the identity. Getting the boundary right removes every `if i == 0` branch from the inner transition, which is where boundary bugs otherwise hide.

It is the opening balance on a ledger: zero transactions posted, not the first transaction.

saying these in an interview costs you the question

  • Says dp[0] holds the answer for the first item
  • Fills the whole table with zeros without checking the operation
  • Seeds a counting DP's empty prefix with 0 instead of 1
  • Seeds unreachable cells with 0 in a minimisation table
  • Adds an if-first-iteration branch instead of fixing the base case
  • Cannot say in one sentence what the index counts

context