Back to Home

Dynamic Programming

Dynamic Programming breaks complex problems into overlapping subproblems, storing solutions to avoid redundant computation. Master the art of identifying optimal substructure and building solutions from smaller pieces.

Key Concepts

Optimal Substructure

An optimal solution contains optimal solutions to its subproblems. Like finding the best route: the best path from A to C through B uses the best path from A to B.

Overlapping Subproblems

The same subproblems recur multiple times. Instead of recomputing, we store results (memoization) or build bottom-up (tabulation).

State Transition

Define how to compute the current state from previous states. This recurrence relation is the heart of any DP solution.

Two Approaches

Top-Down (Memoization)

Start from the main problem, recursively solve subproblems, and cache results.

def fib(n, memo={}):
    if n in memo: return memo[n]
    if n <= 1: return n
    memo[n] = fib(n-1) + fib(n-2)
    return memo[n]

Bottom-Up (Tabulation)

Solve smallest subproblems first, build up to the main problem iteratively.

def fib(n):
    dp = [0, 1]
    for i in range(2, n + 1):
        dp.append(dp[i-1] + dp[i-2])
    return dp[n]