Dynamic Programming Guide Sample
Dynamic Programming Guide Sample
D NAMIC
G AMMING
ATALYK AKASH
The a hor of D namic Programming Pa erns
Disclaimer
This ebook is licensed for your personal enjoyment only. This ebook may not be re-sold or given away
to other people. If you would like to share this book with another person, please purchase an
additional copy for each recipient. If you’re reading this book and did not purchase it, or it was not
purchased for your use only, then please purchase your own copy. Thank you for respecting the hard
work of this author.
The information contained within this eBook is strictly for educational purposes. If you wish to apply
the ideas contained in this eBook, you are taking full responsibility for your actions. The author has
made every effort to ensure the accuracy of the information within this book was correct at the time of
publication. The author does not assume and hereby disclaims any liability to any party for any loss,
damage, or disruption caused by errors or omissions, whether such errors or omissions result from
accident, negligence, or any other cause.
No part of this eBook may be reproduced or transmitted in any form or by any means, electronic or
mechanical, recording, or by any information storage and retrieval system, without written permission
from the author.
𝐹(𝑜𝑝𝑡𝑖𝑜𝑛 1)
𝐹(𝑛) → 𝐹(𝑜𝑝𝑡𝑖𝑜𝑛 1)
…
𝐹(𝑜𝑝𝑡𝑖𝑜𝑛 𝑘)
Fibonacci Sequence
𝐹(𝑙𝑎𝑠𝑡 𝑡𝑒𝑟𝑚)
𝐹(𝑐𝑢𝑟𝑟𝑒𝑛𝑡 𝑡𝑒𝑟𝑚) →
𝐹(𝑜𝑛𝑒 𝑏𝑒𝑓𝑜𝑟𝑒 𝑡ℎ𝑒 𝑙𝑎𝑠𝑡 𝑡𝑒𝑟𝑚)
Unique Paths
𝐹(𝑚𝑜𝑣𝑒 𝑢𝑝)
𝐹(𝑥, 𝑦) →
𝐹(𝑚𝑜𝑣𝑒 𝑙𝑒𝑓𝑡)
There are two options (directions) to choose to reach the target position:
• move up
• move left
14
Step 2. Reduce Target
Choosing every option, we reduce a target to a particular value, and we call a recursive function
again with the new updated target.
Fibonacci Sequence
𝐹(𝑛 − 1)
𝐹(𝑛) →
𝐹(𝑛 − 2)
• Choosing the last term 𝐹(𝑛 − 1) reduces the problem to find (𝑛 − 1)!" term.
• Choosing the one before the last term 𝐹(𝑛 − 2) reduces the problem to find (𝑛 − 2)!" term.
𝐹(1)
𝐹(2) →
𝐹(0)
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 5)
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡) → 𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 3)
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 1)
• Choosing the coin with denomination 5 reduces the problem to make the new target amount of
𝑡𝑎𝑟𝑔𝑒𝑡 − 5.
• Choosing the coin with denomination 3 reduces the problem to make the new target amount of
𝑡𝑎𝑟𝑔𝑒𝑡 − 3.
• Choosing the coin with denomination 1 reduces the problem to make the new target amount of
𝑡𝑎𝑟𝑔𝑒𝑡 − 1.
𝐹(11 − 5)
𝐹(11) → 𝐹 (11 − 3)
𝐹(11 − 1)
𝐹(6)
𝐹(11) → 𝐹 (8)
𝐹(10)
Unique Paths
15
𝐹(𝑥 − 1, 𝑦)
𝐹(𝑥, 𝑦) →
𝐹(𝑥, 𝑦 − 1)
• Choosing the option to move up reduces the problem to find the number of unique paths to
reach (𝑥 − 1, 𝑦).
• Choosing the option to move left reduces the problem to find the number of unique paths to
reach (𝑥, 𝑦 − 1).
𝐹(1,2)
𝐹(2,2) →
𝐹(2,1)
Fibonacci Sequence
Choose the option that leads to the most optimal solution and add one to use one coin for the current
state.
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 5)
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡) = 𝑚𝑖𝑛 I𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 3)J + 1
𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 1)
Unique Paths
16
Step 4. Base Case
When we reach a base case, we need to quit a recursive function. The base case can be a base state
or case when a sub-problem is no longer valid (in this case, when it returns the worst result).
Fibonacci Sequence
We already know that the first two Fibonacci numbers are 0 and 1 respectively.
𝐹(1) = 1
𝐹(0) = 0
1 int F(n) {
2 if (n == 0) {
3 // base case
4 }
5 if (n == 1) {
6 // base case
7 }
8 }
Coin Change
When 𝑡𝑎𝑟𝑔𝑒𝑡 equals 0 (we don’t need any coin to make 0) or less than 0, the function reaches the
base case.
𝐹(0) = 0
1 int F(target) {
2 if (target == 0) {
3 // base case
4 }
5 if (target < 0) {
6 // not valid
7 }
8 }
Unique Paths
When the position equals the destination cell or the position is out of bounds, the function reaches the
base case.
𝐹(0, 0) = 1
17
1 int F(x, y) {
2 if (x == 0 && y == 0) {
3 // base case
4 }
5 if (x < 0 || y < 0) {
6 // not valid
7 }
8 }
return result
1 int Fib(int n) {
2 if (n == 0) {
3 return 0; // step 4
4 }
5 if (n == 1) {
6 return 1; // step 4
7 }
8
9 int last = Fib(n-1); // step 1 & 2
10 int beforeLast = Fib(n-2); // step 1 & 2
11 int result = last + beforeLast; // step 3
12
13 return result; // step 5
14 }
18
8 int substateSolution = CoinChange(target-coins[i], coins); // step 2
9 result = min(result, substateSolution+1); // step 3
10 }
11 }
12 return result; // step 5
13 }
The problem with these recursive solutions is the recalculation of sub-problems, which leads to slow
performance.
19
How to calculate the time complexity of a recursive solution
To calculate the time-complexity of a recursive solution, try to answer the following questions:
• How many times does a function call itself (𝑡)?
• How many times is a function being recursed (𝑘)?
Based on that, we can say that the time complexity of a plain recursive solution is exponential 𝑂(𝑡 # ).
If we draw a recursive tree, we can find the time complexity by summing up the number of nodes in
the tree.
Fibonacci Sequence
To find 𝑛!" Fibonacci number, we need to sum up two previous Fibonacci numbers.
• 𝐹(𝑛 − 1)
• 𝐹(𝑛 − 2)
Then it’s being recursed 𝑛 times from 𝑛 to 0, where 𝑛 is the depth of the recursive tree for the
function.
Let’s calculate the time complexity of the function to see if the statement above is true.
Let’s denote 𝑇(𝑛) as a function to calculate the time complexity of the Fibonacci Sequence.
20
𝑇(𝑛) = 𝑇(𝑛 − 1) + 𝑇(𝑛 − 2) + 𝐶 ,
where 𝑇(𝑛 − 1) and 𝑇(𝑛 − 2) are the time complexities to find 𝑛 − 1!" Fibonacci and 𝑛 − 2!" Fibonacci
numbers respectively, and 𝐶 is the number of constant operations performed inside the recursive call.
𝑇(𝑛) = 2 × 𝑇(𝑛 − 1) + 𝐶
𝑇(𝑛 − 1) = 2 × 𝑇(𝑛 − 2) + 𝐶
𝑇(𝑛) = 2 × (2 × 𝑇(𝑛 − 2) + 𝐶) + 𝐶
𝑇(𝑛) = 22 × 𝑇(𝑛 − 2) + 3𝐶
𝑇(𝑛 − 2) = 2 × 𝑇(𝑛 − 3) + 𝐶
𝑇(𝑛) = 22 × (2 × 𝑇(𝑛 − 3) + 𝐶) + 3𝐶
𝑇(𝑛) = 23 × 𝑇(𝑛 − 3) + 7𝐶
𝑇(0) = 1
𝑛−𝑘 = 0
𝑛 = 𝑘
Using the idea that 𝐹(𝑛) and 𝑇(𝑛) are calculated in the same way, we can find a tighter upper bound
time complexity. Approximate value for 𝐹(𝑛) is 𝑂(𝜙 $ ), where 𝜙 = (1 + √5)/2 ≈ 1.618.
Consequently,
21
Coin Change
• 𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 𝑐𝑜𝑖𝑛 1)
• 𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 𝑐𝑜𝑖𝑛 2)
• 𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 𝑐𝑜𝑖𝑛 3)
• …
• 𝐹(𝑡𝑎𝑟𝑔𝑒𝑡 − 𝑐𝑜𝑖𝑛 𝑛)
So, the time complexity of the plain recursive solution is 𝑂(𝑛!%&'(! %*+,$! ).
Unique Paths
• 𝐹(𝑥 − 1, 𝑦)
• 𝐹(𝑥, 𝑦 − 1)
Then it’s being recursed 𝑛 + 𝑚 times, where 𝑛 is the number of rows and 𝑚 is the number of columns.
For every recursive call, we move one step up, or one step left. In other words, to reach leaf nodes, we
need to exhaust total rows and total columns, meaning that the depth of our recursive tree will be 𝑛 +
𝑚.
22
Figure 9. The recursive tree for Unique Paths.
So, the time complexity of the plain recursive solution is 𝑂(2$-* ) ≈ 𝑂(2$ )..
All the recursive solutions above lead to exponential time complexity due to overlapping sub-
problems.
23