Dynamic Programming - Coin Change
All diagrams presented herein are original creations, meticulously designed to enhance comprehension and recall. Crafting these aids required considerable effort, and I kindly request attribution if this content is reused elsewhere.
Difficulty : Easy
DP
Problem
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.
Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
Example 1:
1
2
3
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:
1
2
Input: coins = [2], amount = 3
Output: -1
Example 3:
1
2
Input: coins = [1], amount = 0
Output: 0
Solution
DFS/Backtracking Solution
We will first find out how to solve this problem using DFS/Backtracking solution. Consider an example where coins=[1,2] and amount= 3. At every step until we have reached or exceeded the amount we can keep adding each of the coins to the total. Then once we have reached the total we can find the min path.
We will start by defining the min_path variable.
1
min_path=float("inf")
Now define the dfs() function. It takes the remaining amount and the current path as inputs. If the remaining amount is less then amount we return however if its same as remaining then we find the len(path) and update min_path if needed.
1
2
3
4
5
6
def dfs(remaining,path):
nonlocal min_path
if remaining < 0:
return
if remaining==0:
min_path=min(min_path,len(path))
Now loop through all the coins and add the each coin to path then after recursively calling dfs() backtrack the added coin from the path.
1
2
3
4
for coin in coins:
path.append(coin)
dfs(remaining - coin, path)
path.pop()
Finally, call dfs() and return min_path.
1
2
dfs(amount,[])
return min_path if min_path!=float("inf") else -1
Even though this is a proper solution, however the time complexity is very high $N^{\text{amount}}$ , where N is the number of different coin denominations. There is a more complex way of implementing this using memoization, however we are going to directly look into the DP solution next.
Dynamic Programming
The idea again comes down to breaking the problem in smaller problem. Like we have done in the previous problems, let’s consider coins = [1,2,5] and amount = 7 as an example.
We need to find out, whats the fewest number of coins to make 7? Since we have coins with value 1,2,5 we can subtract each coin from 7. Referring the diagram below, if we know the fewest number of coins to make 6,5 & 2 when we can just add 1 to that number and find the min among them.
If we know that the fewest number of coins to make 6,5 & 2 are respectively 2,1 and 1 then we can add 1 to that number as we have used 1,2,5 once (pink arrow below). So the final values are 3,2,2. Now we can take min among them and the final result is 2. (Either 7-2=5 or 7-5=2)
The only unknown part here is how do we know the fewest number of coins to make 6,5 & 2? These numbers can also be anything (based on the coins array). However we know that these numbers will always be less than amount. So if we can have the fewest number of coins to make all the values from 0 - (amount-1) then in the final step will need just O(n) to find the final result. (Here n is length of coins).
In this example, we will calculate all the values in a loop. (Refer below).
This is the main logic, where we have two for loops. One for finding fewest number of coins to make for each amounts from 0 - amount.
1
2
3
4
for prev_amount in range(1, amount+1):
for coin in coins:
if prev_amount - coin >=0:
cache[prev_amount]=min(cache[prev_amount], 1+ cache[prev_amount - coin])
Initially we will create the cache with float("inf") values with length amount+1 and will set cache[0]=0.
1
2
cache = [float("inf")]*(amount+1)
cache[0]=0
Following are the patterns used here to find the DP solution.
- 1D DP Problem
- 2D DP Problem
- Backward
- Break into smaller problem
- PreCompute values
- Caching
Final Code
Here is the full code.
1
2
3
4
5
6
7
8
9
10
def coin_change(coins, amount):
cache = [float("inf")]*(amount+1)
cache[0]=0
for prev_amount in range(1, amount+1):
for coin in coins:
if prev_amount - coin >=0:
cache[prev_amount]=min(cache[prev_amount], 1+ cache[prev_amount - coin])
return cache[amount] if cache[amount]!=float("inf") else -1



