Given an unlimited supply of coins of given denominations, find the total number of distinct ways to get the desired change.

For example,

Input: S = { 1, 3, 5, 7 }, target = 8
 
The total number of ways is 6
 
{ 1, 7 }
{ 3, 5 }
{ 1, 1, 3, 3 }
{ 1, 1, 1, 5 }
{ 1, 1, 1, 1, 1, 3 }
{ 1, 1, 1, 1, 1, 1, 1, 1 }
 
 
Input: S = { 1, 2, 3 }, target = 4
 
The total number of ways is 4
 
{ 1, 3 }
{ 2, 2 }
{ 1, 1, 2 }
{ 1, 1, 1, 1 }

Practice this problem

The idea is to use recursion to solve this problem. We recur to see if the total can be reached by choosing the coin or not for each coin of given denominations. If choosing the current coin results in the solution, update the total number of ways.

Following is the C++, Java, and Python implementation of the idea:

C++


Download  Run Code

Output:

The total number of ways to get the desired change is 7

Java


Download  Run Code

Output:

The total number of ways to get the desired change is 7

Python


Download  Run Code

Output:

The total number of ways to get the desired change is 7

The time complexity of the above solution is exponential since each recursive call is making n recursive calls. It also requires additional space for the call stack.

 
There is an issue with the above solution. The above solution doesn’t always return distinct sets. For example, for set {1, 2, 3}, it returns 7 as some ways are permutations of each other, as shown below:

{1, 1, 1, 1}
{1, 1, 2}, {2, 1, 1}, {1, 2, 1}
{2, 2}
{1, 3}, {3, 1}

How can we get distinct ways?

The idea is somewhat similar to the Knapsack problem. We can recursively define the problem as:

count(S, n, total) = count(S, n, total-S[n]) + count(S, n-1, total);

That is, for each coin.

  1. Include current coin S[n] in solution and recur with remaining change total-S[n] with the same number of coins.
  2. Exclude current coin S[n] from solution and recur for remaining coins n-1.

Finally, return the total ways by including or excluding the current coin. The recursion’s base case is when a solution is found (i.e., change becomes 0) or the solution doesn’t exist (when no coins are left, or total becomes negative). Following is the C++, Java, and Python implementation of the idea:

C++


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Java


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Python


Download  Run Code

Output:

The total number of ways to get the desired change is 4

The time complexity of the above solution is still exponential and requires auxiliary space for the call stack.

 
The problem has an optimal substructure as the problem can be broken down into smaller subproblems, which can further be broken down into yet smaller subproblems, and so on. The problem also clearly exhibits overlapping subproblems, so we will end up solving the same subproblem over and over again. The repeated subproblems can be seen by drawing a recursion tree for higher values of the desired change. We know that problems with optimal substructure and overlapping subproblems can be solved using dynamic programming where the subproblem solutions are memoized rather than computed and again.

Following is the C++, Java, and Python implementation of the idea:

C++


Download  Run Code

Output:

The minimum number of coins required to get the desired change is 4

Java


Download  Run Code

Output:

The minimum number of coins required to get the desired change is 4

Python


Download  Run Code

Output:

The minimum number of coins required to get the desired change is 4

The time complexity of the above top-down solution is O(n.target), where n is the total number of coins and target is the total change required. The auxiliary space required by the program is O(n.target).

 
We can even write a bottom-up version of the above memoized solution. The following code shows how to implement this in C, Java, and Python:

C


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Java


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Python


Download  Run Code

Output:

The total number of ways to get the desired change is 4

The time and space complexity of the above bottom-up solution remains the same as the memoized recursive solution. However, the space complexity can be reduced to O(target) using the simplified algorithm below:

C


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Java


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Python


Download  Run Code

Output:

The total number of ways to get the desired change is 4

Exercise: Find the total number of ways to get the desired change from the limited supply of coins of given denominations.