BA-Mastering Dynamic Programming
BA-Mastering Dynamic Programming
BAHUJA
HARAT
BECOME
FUTURE READY.
Here's how you can progress from simple recursive problems to more
complex ones, and eventually apply DP techniques effectively.
BECOME
FUTURE READY.
Now, imagine you're given a coin problem. You have a bag of coins with
different denominations, and you want to know if you can make a certain
sum using a combination of these coins. Let's think of a recursive function
as a "black box" that solves this problem for us.
BECOME
FUTURE READY.
This structure naturally leads to the idea of Memoization, which is a way
to "cache" or store the results of the function calls so that you don’t have
to recalculate them each time. Only changing parameter in function is n
so we can make a cache on n.
Memoization:
To optimize, use memoization to store results of subproblems:
```python
def fib_memo(n, memo={}):
if n <= 1:
return n
if n not in memo:
memo[n] = fib_memo(n-1, memo) +
fib_memo(n-2, memo)
return memo[n]
```
Tabulation:
In memoization, each cached value depends on the smaller cached values. For example,
in the Fibonacci sequence, fib(n) depends on fib(n-1) and fib(n-2). When you cache
fib(n-1) and fib(n-2), the next value, fib(n), can be computed without recalculating those
smaller values.
This idea of building solutions based on smaller, previously computed results naturally
leads to tabulation. Instead of starting with the large problem (n) and working down, as
in memoization, tabulation works in the opposite direction: you start from the smallest
base cases and iteratively build up the solution, storing each result in a table.
By storing solutions for smaller subproblems first, you create a sequence of values that
progressively builds up to the final result—efficiently and without recursion. This is the
essence of the bottom-up approach used in tabulation
BECOME
FUTURE READY.
Convert the recursive solution into an iterative one using a table:
```python
def fib_tab(n):
if n <= 1:
return n
dp = [0] * (n+1)
dp[1] = 1
for i in range(2, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
```
Example 2: Factorial Calculation
Simple Recursion:
The factorial of a number `n` is `n! = n * (n-1)!`:
```python
def factorial(n):
if n == 0:
return 1
return n * factorial(n-1)
```
Memoization:
Memoize factorial calculations to avoid redundant work:
```python
def factorial_memo(n, memo={}):
if n in memo:
return memo[n]
if n == 0:
memo[n] = 1
return 1
memo[n] = n * factorial_memo(n-1, memo)
return memo[n]
```
BECOME
FUTURE READY.
Tabulation:
Create a table to compute factorials iteratively:
```python
def factorial_tab(n):
dp = [1] * (n+1)
for i in range(2, n+1):
dp[i] = dp[i-1] * i
return dp[n]
```
BECOME
FUTURE READY.
Tabulation:
Use a table for the iterative solution:
```python
def lcs_tab(X, Y):
m = len(X)
n = len(Y)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if X[i-1] == Y[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
```
Simple Recursion:
A naive recursive solution involves trying every coin denomination:
```python
def coin_change(coins, amount):
if amount == 0:
return 0
if amount < 0:
return float('inf')
min_coins = float('inf')
for coin in coins:
result = coin_change(coins, amount - coin)
if result != float('inf'):
min_coins = min(min_coins, result + 1)
return min_coins
```
BECOME
FUTURE READY.
Memoization:
Optimize with memoization to store results of subproblems:
```python
def coin_change_memo(coins, amount, memo={}):
if amount in memo:
return memo[amount]
if amount == 0:
return 0
if amount < 0:
return float('inf')
min_coins = float('inf')
for coin in coins:
result = coin_change_memo(coins, amount - coin,
memo)
if result != float('inf'):
min_coins = min(min_coins, result + 1)
memo[amount] = min_coins
return min_coins
```
Tabulation:
Use a table to solve the problem iteratively:
```python
def coin_change_tab(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i in range(1, amount + 1):
for coin in coins:
if i - coin >= 0:
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
```
BECOME
FUTURE READY.
Complex Problems: For more complex problems, visualizing every
recursion step becomes challenging. Focusing on the black box approach
helps simplify problem reduction and optimization without relying on
detailed visualizations.
Conclusion
Once you’ve covered the basics, try the following problems and explore the
different variations you might encounter in interviews. Start with easy
ones and gradually work your way up to harder problems. Always begin by
writing the recursive solution first, then move on to memoization, and
finally, tackle the problem using dynamic programming with tabulation.
Don’t skip any steps
BECOME
FUTURE READY.
Classical DP vv-imp
Knapsack DP vv-imp
Grid DP vv-imp
Dp on Strings vv-imp
Interval DP here
BECOME
FUTURE READY.
Hi, I’m Bharat! With 10 years of experience at
Microsoft, I’ve created this resource to help you
succeed. These insights are drawn from my
industry experience, so you can trust that you’re
getting expert guidance every step of the way.
Follow
For More
BECOME
FUTURE READY.