0% found this document useful (0 votes)
11 views

BA-Mastering Dynamic Programming

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

BA-Mastering Dynamic Programming

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

BHARAT AHUJA FUTURE READY

BAHUJA
HARAT

BHARAT AHUJA 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.

Dynamic Programming (DP) is an important topic frequently asked in


interviews at companies like Google, and many candidates tend to struggle with
it. However, it doesn't have to be as difficult as it seems.
To excel in Dynamic Programming (DP), a strong understanding of
recursion is essential. Contrary to what you might often see, instead of relying
heavily on visual animations, treating recursion as a "black box" can simplify
your learning process.
There are two key reasons for this:
1. You won’t have access to animation tools during an interview
2. For complex problems, visualizing the entire recursion tree can become
difficult and overwhelming.
By doing so, you can build a mental framework that allows you to solve
problems step by step.

BECOME
FUTURE READY.
Here's how you can progress from simple recursive problems to more
complex ones, and eventually apply DP techniques effectively.

1. Understanding Recursion as a Black Box


Recursion can be treated as a "black box" that breaks a problem into smaller
subproblems, solves them, and combines the results. This abstraction allows you to
focus on reducing and combining problems without getting bogged down by the
details of individual recursive calls. This approach is crucial not only for solving
complex problems but also for improving your skills in Dynamic Programming
(DP).

2. Mastering Recursion and Its Stack Dynamics


Understanding the internals of recursion—such as how the call stack manages
function calls and the critical role of base and recursive cases—is essential for
solving problems efficiently. Recursion allows you to break down complex
problems into simpler sub-problems, which the function can solve step by step.
To improve your grasp of recursion, focus on two key elements:
1. The recursive case: This is where the function calls itself, breaking the original
problem into smaller, more manageable pieces. Each recursive step should move
closer to the base case.
2. The base case: This is the condition that stops the recursion from continuing
indefinitely. It's crucial to define a clear base case, as it prevents infinite loops and
ensures the recursion eventually terminates. Think of base case like simple
problem whose answer you can provide without any calculation.

Always think about base case later.

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.

Using the Black Box to Solve the Coin Problem

The function, or the "black box," accepts two inputs:


A target sum.
A list of available coin denominations.
Without needing to know exactly how the function works internally, you
can use it by framing the problem like this:
Recursive case: If the target sum is greater than 0, the function will try using one
of the coins, subtract that coin's value from the target sum, and then call itself
recursively with the new sum.
Base case: If the target sum is 0, you return True because you've successfully
made the sum.
Here’s how you can visualize it:
1. The "black box" function starts by checking the base case: Can we make a sum of
0? If yes, it returns True.
2. If not, it picks a coin and reduces the target sum. The function then calls itself with
the new, smaller target sum (original sum minus the coin value).
3. This process continues, recursively breaking the problem down into smaller
problems, each with a reduced sum, until either the sum is zero (success) or we
run out of options (failure).
Example:
Suppose we want to make a sum of 5 using coins [1, 2, 3]. The "black box" will:
Try using the coin 3, leaving a new target sum of 2 (5 - 3).
The function then calls itself with the target sum 2 and tries to make that using
the same set of coins.
BECOME
FUTURE READY.
It may then use the coin 2, leaving a new sum of 0 (2 - 2), and return True.
This way, by treating the problem as a series of smaller steps and recursively
reducing the sum, the "black box" can solve the coin problem without you needing
to manually handle all the details at each step. You just need to know how to define
the base and recursive cases properly.

3. From Simple Recursion to Dynamic Programming


Let’s explore how to evolve from simple recursive solutions to more complex ones
and apply DP techniques, including the Coin Change problem. Focus on parameters
function takes , how those parameters change in each function call, it will decide
how recursion will converge, and help you figure out how to cache result and later
build table
Example 1: Fibonacci Sequence
Simple Recursion:
The Fibonacci sequence is a classic example where each number is the sum of the
two preceding ones. The recursive solution is straightforward but inefficient:
```python
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
```
This approach recalculates the same values multiple times, leading to exponential
time complexity.
Focus on the Parameters:
The function only depends on one parameter: n.
The value of n is reduced in each recursive call until it reaches the base case
(n <= 1).

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]
```

Example 3: Longest Common Subsequence (LCS)


Simple Recursion:
Finding the LCS of two strings is more complex. The recursive approach looks like this:
```python
def lcs(X, Y, m, n):
if m == 0 or n == 0:
return 0
if X[m-1] == Y[n-1]:
return 1 + lcs(X, Y, m-1, n-1)
else:
return max(lcs(X, Y, m-1, n), lcs(X, Y, m, n-1))
```
Memoization:
Optimize with memoization:
```python
def lcs_memo(X, Y, m, n, memo={}):
if (m, n) in memo:
return memo[(m, n)]
if m == 0 or n == 0:
memo[(m, n)] = 0
elif X[m-1] == Y[n-1]:
memo[(m, n)] = 1 + lcs_memo(X, Y, m-1, n-1, memo)
else:
memo[(m, n)] = max(lcs_memo(X, Y, m-1, n, memo),
lcs_memo(X, Y, m, n-1, memo))
return memo[(m, 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]
```

Example 4: Coin Change Problem


Problem Statement:
Given a set of coin denominations and a total amount, find the minimum number of
coins needed to make the amount. If it is not possible to make the amount, return `-1`.

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
```

4. Why Animation-Based Visualization Falls Short - Interview


Settings:
In coding interviews, you can’t rely on animations. Instead, you need to conceptualize
recursion and DP techniques directly.

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

Mastering Dynamic Programming through recursion involves


understanding recursion as a black box, mastering its stack dynamics, and
applying memoization and tabulation techniques. While animations can
aid in learning, focusing on how to handle problems without visual aids
prepares you for real-world scenarios and interviews. By practicing these
techniques with simple to complex examples, including the Coin Change
problem, you’ll develop a robust approach to solving DP problems
efficiently.

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

Decision Making DP vv-imp

Distinct Ways DP vv-imp

Max Min DP vv-imp

Grid DP vv-imp

Math realted DP here

Dp on Strings vv-imp

Graph/ Tree DP here

Bit Manipulation Dp here

Interval DP here

Merging Interval DP here

Multi Dimesion 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.

You might also like