Learn DSA · Lesson 9 of 11

Dynamic Programming

Stop recomputing what you already worked out.

The idea

Dynamic programming applies when a problem has two properties: an optimal substructure (the best answer is built from best answers to smaller versions) and overlapping subproblems (those smaller versions recur). Without the second, plain recursion is already fine.

There are two ways to write the same thing:

  • Top-down — write the recursion, add a cache. Closest to how you reasoned about the problem, and it only computes the subproblems actually reached.
  • Bottom-up — fill a table from the smallest case upward. No recursion stack, and it often reveals that you only need the last row or two, which drops the space from O(n) to O(1).

The hard part is never the code. It is naming the state: what exactly does best[i] mean? Get that sentence precise and the recurrence is usually one line. Leave it vague and you will write a table you cannot debug.

Greedy is the tempting shortcut, and it fails on coin systems like [1, 3, 4] where making 6 greedily takes 4+1+1 instead of 3+3. DP considers the alternatives greedy discards.

Walkthrough

The table filling row by row. Each cell is the one above plus the one to its left — the recurrence, made visible. Watch the edges fill with 1s first, because there is exactly one way to walk a straight line.

What it costs

time
O(states × work per state)
space
O(states), often reducible to O(1) rows

The gain over naive recursion is usually exponential to polynomial, because a branching call tree collapses into one entry per distinct state.

When to reach for it

Rather than the obvious alternative

Greedy

Far cheaper when it is correct. DP is what you fall back to when a locally best choice can block a globally best answer — a single counterexample decides which you are in.

Plain recursion

The same recurrence without memoisation. For a branching call tree the difference is exponential against polynomial, and it is one cache away.

Backtracking

Use backtracking when you need the arrangements themselves; DP when you only need the best value or a count, and the states repeat.

How to spot it

Where it goes wrong

A vague state definition

If you cannot say in one sentence what `dp[i]` means, the recurrence will be wrong in a way that is very hard to see.

Wrong base cases

The table's first entries are the whole foundation. An off-by-one there is consistently wrong everywhere and looks like a logic bug.

Iterating in the wrong order

Bottom-up requires every dependency to be filled before it is read. Getting the loop order wrong reads zeroes and reports plausible nonsense.

Practise it