🔹 What is Dynamic Programming?
Dynamic Programming is a method for solving complex problems by breaking them down
into simpler subproblems. It is applicable when the problem has:
Overlapping Subproblems: The same subproblems are solved multiple times.
Optimal Substructure: The optimal solution to a problem can be built from optimal
solutions of its subproblems.
🔹 Key Techniques
1. Memoization (Top-Down)
Solve the problem recursively.
Store results of subproblems in a data structure (usually an array or a hash map)
to avoid redundant computations.
2. Tabulation (Bottom-Up)
Solve all possible subproblems starting from the smallest.
Use an iterative approach to build the solution from the bottom up.
🔹 Steps to Solve a DP Problem
Define subproblem: What does the problem depend on?
Write the recurrence relation: How is the current state related to previous states?
Determine base cases: What's the simplest version of the problem?
Choose memoization or tabulation: Decide the approach.
Implement: Carefully code the logic, often using arrays or matrices.
🔹 Classic Examples
1. Fibonacci Sequence
python
Copy
Edit
# Memoization
def fib(n, memo={}):
if n in memo: return memo[n]
if n <= 1: return n
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]
2. 0/1 Knapsack Problem
Choose to include or exclude an item to maximize value within a weight limit.
Uses a 2D DP array for values based on items and capacity.
3. Longest Common Subsequence (LCS)
Find the longest sequence common to two strings.
Use a 2D DP table to build up solutions.
🔹 Tips
Identify repeated work in recursion → it’s a DP candidate.
Use visualization (tables or recursion trees) to understand the problem.
Optimize space where possible (e.g., from 2D to 1D arrays).