← Back to DSA Course | Chapter 18: Interview Preparation | Lesson 3 of 4

Top DP Problems

DP Idea

Dynamic programming's core idea is to store the answer to each distinct subproblem the first time it's solved, so any later request for that same subproblem returns instantly instead of being recomputed from scratch.

Example: DP Idea

#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<int,int> cache;
int fib(int n) {
	if (n <= 1) return n;
	if (cache.count(n)) return cache[n];
	return cache[n] = fib(n-1) + fib(n-2);
}
int main() { cout << "Stored once, reused instantly on later requests: fib(15) = " << fib(15); return 0; }
import java.util.*;
public class Main {
	static Map<Integer,Integer> cache = new HashMap<>();
	static int fib(int n) {
		if (n <= 1) return n;
		if (cache.containsKey(n)) return cache.get(n);
		int r = fib(n-1) + fib(n-2);
		cache.put(n, r);
		return r;
	}
	public static void main(String[] args) { System.out.println("Stored once, reused instantly on later requests: fib(15) = " + fib(15)); }
}
cache = {}
def fib(n):
    if n <= 1:
        return n
    if n in cache:
        return cache[n]
    cache[n] = fib(n-1) + fib(n-2)
    return cache[n]
print("Stored once, reused instantly on later requests: fib(15) =", fib(15))
#include <stdio.h>
int cache[16]={0}, computed[16]={0};
int fib(int n) {
	if (n <= 1) return n;
	if (computed[n]) return cache[n];
	computed[n] = 1;
	return cache[n] = fib(n-1) + fib(n-2);
}
int main() { printf("Stored once, reused instantly on later requests: fib(15) = %d", fib(15)); return 0; }

Fibonacci

Fibonacci is the simplest illustration of overlapping subproblems: naive recursion recomputes the same smaller Fibonacci values repeatedly, and storing each value the first time fixes that redundancy immediately.

Example: Fibonacci

#include <iostream>
using namespace std;
int main() {
	int dp[11]; dp[0]=0; dp[1]=1;
	for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
	cout << "fib(10) = " << dp[10] << " built bottom-up, no recomputation";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] dp = new int[11]; dp[0]=0; dp[1]=1;
		for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
		System.out.println("fib(10) = " + dp[10] + " built bottom-up, no recomputation");
	}
}
dp = [0]*11
dp[1] = 1
for i in range(2, 11):
    dp[i] = dp[i-1] + dp[i-2]
print("fib(10) =", dp[10], "built bottom-up, no recomputation")
#include <stdio.h>
int main() {
	int dp[11]; dp[0]=0; dp[1]=1;
	for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
	printf("fib(10) = %d built bottom-up, no recomputation", dp[10]);
	return 0;
}

Climbing Stairs

The number of distinct ways to climb a staircase (taking 1 or 2 steps at a time) at step n is built directly from the number of ways to reach step n-1 and step n-2 — the exact same recurrence shape as Fibonacci, just framed differently.

Example: Climbing Stairs

#include <iostream>
using namespace std;
int main() {
	int n = 5;
	int dp[6]; dp[0]=1; dp[1]=1;
	for (int i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
	cout << "Ways to climb " << n << " stairs (1 or 2 at a time): " << dp[n];
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int n = 5;
		int[] dp = new int[6]; dp[0]=1; dp[1]=1;
		for (int i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
		System.out.println("Ways to climb " + n + " stairs (1 or 2 at a time): " + dp[n]);
	}
}
n = 5
dp = [0]*(n+1)
dp[0] = dp[1] = 1
for i in range(2, n+1):
    dp[i] = dp[i-1] + dp[i-2]
print(f"Ways to climb {n} stairs (1 or 2 at a time): {dp[n]}")
#include <stdio.h>
int main() {
	int n = 5;
	int dp[6]; dp[0]=1; dp[1]=1;
	for (int i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
	printf("Ways to climb %d stairs (1 or 2 at a time): %d", n, dp[n]);
	return 0;
}

Knapsack

Knapsack-style DP problems choose a subset of items to maximize value while respecting a capacity constraint, deciding for each item whether including it is worth the value gained against the extra capacity it consumes.

Example: Knapsack

#include <iostream>
using namespace std;
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	int dp[6] = {0};
	for (int i = 0; i < 3; i++)
		for (int w = capacity; w >= weights[i]; w--)
			dp[w] = max(dp[w], dp[w-weights[i]] + values[i]);
	cout << "Best value under capacity " << capacity << ": " << dp[capacity];
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] weights = {2,3,4}, values = {3,4,5};
		int capacity = 5;
		int[] dp = new int[6];
		for (int i = 0; i < 3; i++)
			for (int w = capacity; w >= weights[i]; w--)
				dp[w] = Math.max(dp[w], dp[w-weights[i]] + values[i]);
		System.out.println("Best value under capacity " + capacity + ": " + dp[capacity]);
	}
}
weights, values, capacity = [2,3,4], [3,4,5], 5
dp = [0]*(capacity+1)
for i in range(3):
    for w in range(capacity, weights[i]-1, -1):
        dp[w] = max(dp[w], dp[w-weights[i]] + values[i])
print("Best value under capacity", capacity, ":", dp[capacity])
#include <stdio.h>
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	int dp[6] = {0};
	for (int i = 0; i < 3; i++)
		for (int w = capacity; w >= weights[i]; w--)
			if (dp[w-weights[i]] + values[i] > dp[w]) dp[w] = dp[w-weights[i]] + values[i];
	printf("Best value under capacity %d: %d", capacity, dp[capacity]);
	return 0;
}

DP Practice

Before writing any DP code, it helps to explicitly answer four questions: what does a state represent, how does a larger state get built from smaller ones, what are the base cases, and where does the final answer come from in the table.

Example: DP Practice

#include <iostream>
using namespace std;
int main() {
	cout << "4 questions: what is a state, how is it built from smaller states, base cases, iteration order";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("4 questions: what is a state, how is it built from smaller states, base cases, iteration order");
	}
}
print("4 questions: what is a state, how is it built from smaller states, base cases, iteration order")
#include <stdio.h>
int main() {
	printf("4 questions: what is a state, how is it built from smaller states, base cases, iteration order");
	return 0;
}
🔒

Chapter Quiz — Complete all 4 topics to unlock

0/4 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.