Mastering Dynamic Programming: Detailed Guide with 7 Essential Problems and Solutions
Hey everyone, it’s alanturrr1703 back again! 😄 Today, we are diving deep into the world of Dynamic Programming (DP), a vital technique in problem-solving, especially in competitive programming and complex real-world applications. If you’ve been struggling with DP or want to sharpen your skills, this 20-minute read will help you understand and solve DP problems step by step.
We’ll cover seven essential DP problems including:
- Fibonacci Sequence
- 0/1 Knapsack Problem
- Longest Common Subsequence (LCS)
- Shortest Paths in Graphs (Bellman-Ford and Floyd-Warshall)
- Matrix Chain Multiplication
- Traveling Salesman Problem (TSP)
- Edit Distance
For each problem, we’ll explore both top-down (memoization) and bottom-up (tabulation) approaches with detailed explanations and pseudocode.
Let’s jump in! 🚀
1. Fibonacci Sequence (Classic Problem)
Problem Statement:
Find the n-th Fibonacci number, where the sequence is defined as:
- Fib(0) = 0
- Fib(1) = 1
- Fib(n) = Fib(n-1) + Fib(n-2) for n ≥ 2
Top-Down Approach (Memoization):
In the top-down approach, we break the problem into subproblems recursively and store results in a memoization table to avoid recomputing the same values.
Function Fibonacci(n):
memo = array of size n + 1 initialized to -1
Function Fib(n):
If n == 0:
Return 0
ElseIf n == 1:
Return 1
If memo[n] != -1:
Return memo[n]
memo[n] = Fib(n-1) + Fib(n-2)
Return memo[n]
Return Fib(n)
Bottom-Up Approach (Tabulation):
In the bottom-up approach, we iteratively calculate Fibonacci numbers from the smallest subproblem up to n
.
Function FibonacciDP(n):
If n == 0:
Return 0
If n == 1:
Return 1
dp = array of size n + 1
dp[0] = 0
dp[1] = 1
For i = 2 to n:
dp[i] = dp[i-1] + dp[i-2]
Return dp[n]
Time Complexity:
- Time Complexity: O(n)
- Space Complexity: O(n)
2. 0/1 Knapsack Problem
Problem Statement:
Given n
items, each with a weight and a value, and a knapsack with capacity W
, find the maximum value that can be obtained without exceeding the weight limit.
Top-Down Approach (Memoization):
Function Knapsack(W, weights, values, n):
memo = 2D array of size (n + 1) x (W + 1) initialized to -1
Function DP(i, W):
If i == 0 or W == 0:
Return 0
If memo[i][W] != -1:
Return memo[i][W]
If weights[i-1] > W:
memo[i][W] = DP(i-1, W)
Else:
memo[i][W] = max(values[i-1] + DP(i-1, W - weights[i-1]), DP(i-1, W))
Return memo[i][W]
Return DP(n, W)
Bottom-Up Approach (Tabulation):
Function KnapsackDP(W, weights, values, n):
dp = 2D array of size (n + 1) x (W + 1)
For i = 0 to n:
For w = 0 to W:
If i == 0 or w == 0:
dp[i][w] = 0
ElseIf weights[i-1] <= w:
dp[i][w] = max(values[i-1] + dp[i-1][w - weights[i-1]], dp[i-1][w])
Else:
dp[i][w] = dp[i-1][w]
Return dp[n][W]
Time Complexity:
- Time Complexity: O(n * W)
- Space Complexity: O(n * W)
3. Longest Common Subsequence (LCS)
Problem Statement:
Given two strings X
and Y
, find the length of the longest subsequence that is present in both strings.
Top-Down Approach (Memoization):
Function LCS(X, Y, m, n):
memo = 2D array of size (m + 1) x (n + 1) initialized to -1
Function DP(i, j):
If i == 0 or j == 0:
Return 0
If memo[i][j] != -1:
Return memo[i][j]
If X[i-1] == Y[j-1]:
memo[i][j] = 1 + DP(i-1, j-1)
Else:
memo[i][j] = max(DP(i-1, j), DP(i, j-1))
Return memo[i][j]
Return DP(m, n)
Bottom-Up Approach (Tabulation):
Function LCSDP(X, Y, m, n):
dp = 2D array of size (m + 1) x (n + 1)
For i = 0 to m:
For j = 0 to n:
If i == 0 or j == 0:
dp[i][j] = 0
ElseIf X[i-1] == Y[j-1]:
dp[i][j] = 1 + dp[i-1][j-1]
Else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
Return dp[m][n]
Time Complexity:
- Time Complexity: O(m * n)
- Space Complexity: O(m * n)
4. Shortest Paths in Graphs
Problem 1: Bellman-Ford Algorithm
Problem Statement:
Find the shortest path from a source vertex to all other vertices in a graph with possible negative weight edges.
Bellman-Ford Algorithm (Bottom-Up):
Function BellmanFord(vertices, edges, source):
dist = array of size vertices initialized to infinity
dist[source] = 0
For i = 1 to vertices - 1:
For each edge (u, v, weight):
If dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
# Check for negative-weight cycles
For each edge (u, v, weight):
If dist[u] + weight < dist[v]:
Print "Negative weight cycle detected"
Return
Return dist
Time Complexity:
- Time Complexity: O(vertices * edges)
- Space Complexity: O(vertices)
Problem 2: Floyd-Warshall Algorithm
Problem Statement:
Find the shortest path between all pairs of vertices in a graph.
Floyd-Warshall Algorithm:
Function FloydWarshall(graph, vertices):
dist = 2D array of size (vertices x vertices) initialized to infinity
For i = 0 to vertices - 1:
dist[i][i] = 0
For each edge (u, v, weight):
dist[u][v] = weight
For k = 0 to vertices - 1:
For i = 0 to vertices - 1:
For j = 0 to vertices - 1:
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
Return dist
Time Complexity:
- Time Complexity: O(vertices³)
- Space Complexity: O(vertices²)
5. Matrix Chain Multiplication
Problem Statement:
Given a sequence of matrices, find the most efficient way to multiply these matrices together, minimizing the number of scalar multiplications.
Top-Down Approach (Memoization):
Function MatrixChainOrder(p, i, j):
memo = 2D array of size (n x n) initialized to -1
Function DP(i, j):
If i == j:
Return 0
If memo[i][j] != -1:
Return memo[i][j]
memo[i][j] = infinity
For k = i to j-1:
cost = DP(i, k) + DP(k+1, j) + p[i-1] * p[k] * p[j]
memo[i][j] = min(memo[i][j], cost)
Return memo[i][j]
Return DP(1, length(p) - 1)
Bottom-Up Approach (Tabulation):
Function MatrixChainOrderDP(p, n):
dp = 2D array of size (n x n
)
For i = 1 to n:
dp[i][i] = 0
For length = 2 to n:
For i = 1 to n - length + 1:
j = i + length - 1
dp[i][j] = infinity
For k = i to j-1:
cost = dp[i][k] + dp[k+1][j] + p[i-1] * p[k] * p[j]
dp[i][j] = min(dp[i][j], cost)
Return dp[1][n-1]
Time Complexity:
- Time Complexity: O(n³)
- Space Complexity: O(n²)
6. Traveling Salesman Problem (TSP)
Problem Statement:
Given a set of cities and distances between every pair of cities, find the shortest possible route that visits every city exactly once and returns to the starting point.
Top-Down Approach (Memoization with Bitmasking):
Function TSP(mask, pos):
If mask == allVisited:
Return distance[pos][0]
If dp[mask][pos] != -1:
Return dp[mask][pos]
ans = infinity
For city = 0 to n-1:
If city is not visited:
newAns = distance[pos][city] + TSP(mask | (1 << city), city)
ans = min(ans, newAns)
dp[mask][pos] = ans
Return dp[mask][pos]
Time Complexity:
- Time Complexity: O(n² * 2^n)
- Space Complexity: O(n * 2^n)
7. Edit Distance (Levenshtein Distance)
Problem Statement:
Given two strings X
and Y
, find the minimum number of operations (insert, delete, replace) required to convert X
into Y
.
Top-Down Approach (Memoization):
Function EditDistance(X, Y, m, n):
memo = 2D array of size (m+1) x (n+1) initialized to -1
Function DP(i, j):
If i == 0:
Return j
If j == 0:
Return i
If memo[i][j] != -1:
Return memo[i][j]
If X[i-1] == Y[j-1]:
memo[i][j] = DP(i-1, j-1)
Else:
memo[i][j] = 1 + min(DP(i-1, j), DP(i, j-1), DP(i-1, j-1))
Return memo[i][j]
Return DP(m, n)
Bottom-Up Approach (Tabulation):
Function EditDistanceDP(X, Y, m, n):
dp = 2D array of size (m+1) x (n+1)
For i = 0 to m:
For j = 0 to n:
If i == 0:
dp[i][j] = j
ElseIf j == 0:
dp[i][j] = i
ElseIf X[i-1] == Y[j-1]:
dp[i][j] = dp[i-1][j-1]
Else:
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
Return dp[m][n]
Time Complexity:
- Time Complexity: O(m * n)
- Space Complexity: O(m * n)
Conclusion:
In this comprehensive guide, we covered seven essential dynamic programming problems, showing both top-down (memoization) and bottom-up (tabulation) approaches with pseudocode. Mastering these problems will give you the tools to solve a wide range of optimization and combinatorial problems.
The key to dynamic programming lies in understanding the overlapping subproblems and the optimal substructure of the problem. With practice, you’ll be able to recognize DP patterns and apply these techniques to more complex problems.
That’s all for today! I hope this blog helped you understand dynamic programming more deeply. Until next time, happy coding! 🚀