Dynamic programming is applicable when a problem has two properties: overlapping subproblems (the same subproblem is solved multiple times in a naive recursive approach) and optimal substructure (an optimal solution can be constructed from optimal solutions to subproblems).
The core patterns to recognize:
1. 1D DP — State is a single index. Classic examples: Fibonacci, climbing stairs, house robber. Ask: what's the recurrence relating dp[i] to dp[i-1] or dp[i-2]?
2. 2D DP — State is two indices, often (i, j) representing positions in two sequences. Classic: longest common subsequence, edit distance. The table fills left-to-right, top-to-bottom.
3. Interval DP — State is a range [i, j]. Classic: matrix chain multiplication, burst balloons. Usually requires iterating over interval length first.
4. Knapsack variants — Choosing items with constraints. 0/1 knapsack, unbounded knapsack, subset sum. Key: does order matter? Can items repeat?
The mental model shift that makes DP click: stop thinking recursively top-down and start thinking about which previous states you need to answer the current state. Define the state, write the recurrence, handle the base cases, decide the order of iteration.