CJ

Explore

Tracks

My Courses

Edpresso

Refer a Friend

Create

Grokking Dynamic Programming Patterns for Coding Interviews

97% completed

Introduction

What is Dynamic Programming?

Pattern 1: 0/1 Knapsack

0/1 Knapsack

Equal Subset Sum Partition

Subset Sum

Minimum Subset Sum Difference

Count of Subset Sum

Target Sum

Pattern 2: Unbounded Knapsack

Unbounded Knapsack

Rod Cutting

Coin Change

Minimum Coin Change

Maximum Ribbon Cut

Pattern 3: Fibonacci Numbers

Fibonacci numbers

Staircase

Number factors

Minimum jumps to reach the end

Minimum jumps with fee

House thief

Pattern 4: Palindromic Subsequence

Longest Palindromic Subsequence

Longest Palindromic Substring

Count of Palindromic Substrings

Minimum Deletions in a String to make it a Palindrome

Palindromic Partitioning

Pattern 5: Longest Common Substring

Longest Common Substring

Longest Common Subsequence

Minimum Deletions & Insertions to Transform a String into another

Longest Increasing Subsequence

Maximum Sum Increasing Subsequence

Shortest Common Super-sequence

Minimum Deletions to Make a Sequence Sorted

Longest Repeating Subsequence

Subsequence Pattern Matching

Longest Bitonic Subsequence

Longest Alternating Subsequence

Edit Distance

Strings Interleaving

Appendix

Contact Us

Other courses

Mark Course as Completed

Minimum Coin Change

We'll cover the following

Introduction

Problem Statement

Basic Solution

Code

Top-down Dynamic Programming with Memoization

Bottom-up Dynamic Programming

Code

Introduction #

Given an infinite supply of ‘n’ coin denominations and a total money amount, we are asked to find the minimum number of coins needed to make up that amount.

Example 1:

Denominations: {1,2,3}
Total amount: 5
Output: 2
Explanation: We need minimum of two coins {2,3} to make a total of '5'

Example 2:

Denominations: {1,2,3}
Total amount: 11
Output: 4
Explanation: We need minimum four coins {2,3,3,3} to make a total of '11'

Problem Statement #

Given a number array to represent different coin denominations and a total amount ‘T’, we need to find the minimum number of coins needed to make change for ‘T’. We can assume an infinite supply of coins, therefore, each coin can be chosen multiple times.

This problem follows the Unbounded Knapsack pattern.

Basic Solution #

A basic brute-force solution could be to try all combinations of the given coins to select the ones that give a total sum of ‘T’. This is what our algorithm will look like:

for each coin 'c'
create a new set which includes one quantity of coin 'c' if it does not exceed 'T', and
recursively call to process all coins
create a new set without coin 'c', and recursively call to process the remaining coins
return the count of coins from the above two sets with a smaller number of coins

Code #

Here is the code for the brute-force solution:

Java

Java

Java

JS

JS

Python3

Python3

C++

C++

class CoinChange {

public int countChange(int[] denominations, int total) {
int result = this.countChangeRecursive(denominations, total, 0);
return (result == Integer.MAX_VALUE ? -1 : result);
}

private int countChangeRecursive(int[] denominations, int total, int currentIndex) {
// base check
if (total == 0)
return 0;

if(denominations.length == 0 || currentIndex >= denominations.length)
return Integer.MAX_VALUE;

// recursive call after selecting the coin at the currentIndex
// if the coin at currentIndex exceeds the total, we shouldn't process this
int count1 = Integer.MAX_VALUE;
if( denominations[currentIndex] <= total ) {
int res = countChangeRecursive(denominations, total - denominations[currentIndex], currentIndex);
if(res != Integer.MAX_VALUE){
count1 = res + 1;
}
}

// recursive call after excluding the coin at the currentIndex
int count2 = countChangeRecursive(denominations, total, currentIndex + 1);

return Math.min(count1, count2);
}

public static void main(String[] args) {
CoinChange cc = new CoinChange();
int[] denominations = {1, 2, 3};
System.out.println(cc.countChange(denominations, 5));
System.out.println(cc.countChange(denominations, 11));
System.out.println(cc.countChange(denominations, 7));
denominations = new int[]{3, 5};
System.out.println(cc.countChange(denominations, 7));
}
}

RUN

SAVERESET

The time complexity of the above algorithm is exponential O(2C+T)O(2^{C+T})O(2​C+T​​), where ‘C’ represents total coin denominations and ‘T’ is the total amount that we want to make change. The space complexity will be O(C+T)O(C+T)O(C+T).

Let’s try to find a better solution.

Top-down Dynamic Programming with Memoization #

We can use memoization to overcome the overlapping sub-problems. We will be using a two-dimensional array to store the results of solved sub-problems. As mentioned above, we need to store results for every coin combination and for every possible sum:

Java

Java

Java

JS

JS

Python3

Python3

C++

C++

class CoinChange {

public int countChange(int[] denominations, int total) {
Integer[][] dp = new Integer[denominations.length][total + 1];
int result = this.countChangeRecursive(dp, denominations, total, 0);
return (result == Integer.MAX_VALUE ? -1 : result);
}

private int countChangeRecursive(Integer[][] dp, int[] denominations, int total, int currentIndex) {
// base check
if (total == 0)
return 0;

if(denominations.length == 0 || currentIndex >= denominations.length)
return Integer.MAX_VALUE;

// check if we have not already processed a similar sub-problem
if(dp[currentIndex][total] == null) {
// recursive call after selecting the coin at the currentIndex
// if the coin at currentIndex exceeds the total, we shouldn't process this
int count1 = Integer.MAX_VALUE;
if( denominations[currentIndex] <= total ) {
int res = countChangeRecursive(dp, denominations, total - denominations[currentIndex], currentIndex);
if(res != Integer.MAX_VALUE){
count1 = res + 1;
}
}

// recursive call after excluding the coin at the currentIndex
int count2 = countChangeRecursive(dp, denominations, total, currentIndex + 1);
dp[currentIndex][total] = Math.min(count1, count2);
}
return dp[currentIndex][total];
}

public static void main(String[] args) {
CoinChange cc = new CoinChange();
int[] denominations = {1, 2, 3};
System.out.println(cc.countChange(denominations, 5));
System.out.println(cc.countChange(denominations, 11));
System.out.println(cc.countChange(denominations, 7));
denominations = new int[]{3, 5};
System.out.println(cc.countChange(denominations, 7));
}
}

RUN

SAVERESET

Bottom-up Dynamic Programming #

Let’s try to populate our array dp[TotalDenominations][Total+1] for every possible total with a minimum number of coins needed.

So for every possible total ‘t’ (0<= t <= Total) and for every possible coin index (0 <= index < denominations.length), we have two options:

Exclude the coin: In this case, we will take the minimum coin count from the previous set => dp[index-1][t]

Include the coin if its value is not more than ‘t’: In this case, we will take the minimum count needed to get the remaining total, plus include ‘1’ for the current coin => dp[index][t-denominations[index]] + 1

Finally, we will take the minimum of the above two values for our solution:

dp[index][t] = min(dp[index-1][t], dp[index][t-denominations[index]] + 1)

Let’s draw this visually with the following example:

Denominations: [1, 2, 3]
Total: 7

Let’s start with our base case of zero total:

We don't need any coin to make zero total

1 of 14

Total:1, Index:0 => dp[Index][Total-denominations[Index] + 1, we didn't consider dp[Index-1][Total] as Index = 0

2 of 14

Total:2, Index:0 => dp[Index][Total-denominations[Index] + 1, we didn't consider dp[Index-1][Total] as Index = 0

3 of 14

Total:3-7, Index:0 => dp[Index][Total-denominations[Index] + 1, we didn't consider dp[Index-1][Total] as Index = 0

4 of 14

Total:1, Index:1 => dp[Index-1][t], we didn't consider dp[Index][Total-denominations[Index] as Total < denominations[Index]

5 of 14

Total:2, Index:1 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

6 of 14

Total:3, Index:1 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

7 of 14

Total:4-7, Index:1 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

8 of 14

Total:1-2, Index:2 => dp[Index-1][t], we didn't consider dp[Index][Total-denominations[Index] as Total < denominations[Index]

9 of 14

Total:3, Index:2 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

10 of 14

Total:4, Index:2 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

11 of 14

Total:5, Index:2 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

12 of 14

Total:6, Index:2 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

13 of 14

Total:7, Index:2 => min( dp[Index-1][Total], dp[Index][Total-denominations[Index] + 1)

14 of 14

Code #

Here is the code for our bottom-up dynamic programming approach:

Java

Java

Java

JS

JS

Python3

Python3

C++

C++

import java.util.Arrays;

class CoinChange {

public int countChange(int[] denominations, int total)
{
int n = denominations.length;
int[][] dp = new int[n][total + 1];

for(int i=0; i < n; i++)
for(int j=0; j <= total; j++)
dp[i][j] = Integer.MAX_VALUE;

// populate the total=0 columns, as we don't need any coin to make zero total
for(int i=0; i < n; i++)
dp[i][0] = 0;

for(int i=0; i < n; i++) {
for(int t=1; t <= total; t++) {
if(i > 0)
dp[i][t] = dp[i-1][t]; //exclude the coin
if(t >= denominations[i]) {
if(dp[i][t-denominations[i]] != Integer.MAX_VALUE)
dp[i][t] = Math.min(dp[i][t], dp[i][t-denominations[i]]+1); // include the coin
}
}
}

// total combinations will be at the bottom-right corner.
return (dp[n-1][total] == Integer.MAX_VALUE ? -1 : dp[n-1][total]);
}

public static void main(String[] args) {
CoinChange cc = new CoinChange();
int[] denominations = {1, 2, 3};
System.out.println(cc.countChange(denominations, 5));
System.out.println(cc.countChange(denominations, 11));
System.out.println(cc.countChange(denominations, 7));
denominations = new int[]{3, 5};
System.out.println(cc.countChange(denominations, 7));
}
}

RUN

SAVERESET

The above solution has time and space complexity of O(C∗T)O(C*T)O(C∗T), where ‘C’ represents total coin denominations and ‘T’ is the total amount that we want to make change.

Completed

←    Back
Coin Change

Next    →
Maximum Ribbon Cut

Report an Issue

Ask a Question