Open In App

Coin Change – Count Ways to Make Sum

Last Updated : 16 Nov, 2024
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Save
Share
Report
News Follow

Given an integer array of coins[] of size n representing different types of denominations and an integer sum, the task is to count all combinations of coins to make a given value sum.  

Note: Assume that you have an infinite supply of each type of coin. 

Examples: 

Input: sum = 4, coins[] = [1, 2, 3]
Output: 4
Explanation: There are four solutions: [1, 1, 1, 1], [1, 1, 2], [2, 2] and [1, 3]

Input: sum = 10, coins[] = [2, 5, 3, 6]
Output: 5
Explanation: There are five solutions: 
[2, 2, 2, 2, 2], [2, 2, 3, 3], [2, 2, 6], [2, 3, 5] and [5, 5]

Input: sum = 10, coins[] = [10]
Output: 1
Explanation: The only is to pick 1 coin of value 10.

Input: sum = 5, coins[] = [4]
Output: 0
Explanation: We cannot make sum 5 with the given coins

Using Recursion – O(2^sum) Time and O(sum) Space

For each coin, there are 2 options:

  • Include the current coin: Subtract the current coin’s denomination from the target sum and call the count function recursively with the updated sum and the same set of coins i.e., count(coins, n, sum – coins[n-1] )
  • Exclude the current coin: Call the count function recursively with the same sum and the remaining coins. i.e., count(coins, n-1,sum ).

Recurrence Relation: The final result will be the sum of both cases.

  • count(coins, n, sum) = count(coins, n, sum-count[n-1]) + count(coins, n-1, sum)

Base cases:

  • If the target sum (sum) is 0, there is only one way to make the sum, which is by not selecting any coin. So, count(0, coins, n) = 1.
  • If the target sum (sum) is negative or no coins are left to consider (n == coins.size), then there are no ways to make the sum, so count(sum, coins, 0) = 0.
coin---------change_________ C++
// C++ program for coin change problem
// using recursion
#include <bits/stdc++.h>
using namespace std;

// Returns the count of ways we can
// sum coins[0...n-1] coins to get sum "sum"
int countRecur(vector<int>& coins, int n, int sum) {
  
    // If sum is 0 then there is 1 solution
    // (do not include any coin)
    if (sum == 0) return 1;

    // 0 ways in the following two cases
    if (sum < 0 || n == 0) return 0;

    // count is sum of solutions (i)
    // including coins[n-1] (ii) excluding coins[n-1]
    return countRecur(coins, n, sum - coins[n - 1]) + 
            countRecur(coins, n - 1, sum);
}

int count(vector<int> &coins, int sum) {
    return countRecur(coins, coins.size(), sum);
}

int main() {
    vector<int> coins = {1, 2, 3};
    int sum = 5;
    cout << count(coins, sum);
    return 0;
}
Java
// Java program for coin change problem.
// using recursion

class GfG {

    static int countRecur(int[] coins, int n, int sum) {

        // If sum is 0 then there is 1 solution
        // (do not include any coin)
        if (sum == 0) return 1;

        // 0 ways in the following two cases
        if (sum < 0 || n == 0) return 0;

        // count is sum of solutions (i)
        // including coins[n-1] (ii) excluding coins[n-1]
        return countRecur(coins, n, sum - coins[n - 1]) +
                countRecur(coins, n - 1, sum);
    }

    static int count(int[] coins, int sum) {
        return countRecur(coins, coins.length, sum);
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 3};
        int sum = 5;
        System.out.println(count(coins, sum));
    }
}
Python
# Python program for coin change problem.
# using recursion

def countRecur(coins, n, sum):
  
    # If sum is 0 then there is 1 solution
    # (do not include any coin)
    if sum == 0:
        return 1

    # 0 ways in the following two cases
    if sum < 0 or n == 0:
        return 0

    # count is sum of solutions (i)
    # including coins[n-1] (ii) excluding coins[n-1]
    return countRecur(coins, n, sum - coins[n - 1]) + \
              countRecur(coins, n - 1, sum)

def count(coins, sum):
    return countRecur(coins, len(coins), sum)

if __name__ == "__main__":
    coins = [1, 2, 3]
    sum = 5
    print(count(coins, sum))
C#
// C# program for coin change problem.
// using recursion

using System;

class GfG {

    static int countRecur(int[] coins, int n, int sum) {

        // If sum is 0 then there is 1 solution
        // (do not include any coin)
        if (sum == 0) return 1;

        // 0 ways in the following two cases
        if (sum < 0 || n == 0) return 0;

        // count is sum of solutions (i)
        // including coins[n-1] (ii) excluding coins[n-1]
        return countRecur(coins, n, sum - coins[n - 1]) +
               countRecur(coins, n - 1, sum);
    }

    static int count(int[] coins, int sum) {
        return countRecur(coins, coins.Length, sum);
    }

    static void Main() {
        int[] coins = {1, 2, 3};
        int sum = 5;
        Console.WriteLine(count(coins, sum));
    }
}
JavaScript
// JavaScript program for coin change problem.
// using recursion

function countRecur(coins, n, sum) {

    // If sum is 0 then there is 1 solution
    // (do not include any coin)
    if (sum === 0) return 1;

    // 0 ways in the following two cases
    if (sum < 0 || n === 0) return 0;

    // count is sum of solutions (i)
    // including coins[n-1] (ii) excluding coins[n-1]
    return countRecur(coins, n, sum - coins[n - 1]) +
           countRecur(coins, n - 1, sum);
}

function count(coins, sum) {
    return countRecur(coins, coins.length, sum);
}

const coins = [1, 2, 3];
const sum = 5;
console.log(count(coins, sum));

Output
5

Using Top-Down DP (Memoization) – O(sum*n) Time and O(sum*n) Space

1. Optimal Substructure: Number of ways to make sum at index i, i.e., count(i, sum, coins), depends on the optimal solutions of the subproblems count(i, sum-coins[i], coins) , and count(i+1, sum, coins). By adding these optimal substructures, we can efficiently calculate the number of ways to make target sum at index i.

2. Overlapping Subproblems: While applying a recursive approach in this problem, we notice that certain subproblems are computed multiple times.

Follow the below steps to Implement the idea:

  • Since there are two parameters that change during recursive calls, we create a 2D memo array to store the results of previously solved subproblems.
  • Since the range of recursion parameters goes from 0 to n and 0 to sum, we keep dimensions of the 2D array as (n+1) x (sum + 1)
  • memo[i][j] will represent the number of distinct ways to make the sum j by using the first i coins.
  • During the recursion call, if the same state is called more than once, then we can directly return the answer stored for that state instead of calculating again.
C++
// C++ program for coin change problem
// using memoization
#include <bits/stdc++.h>
using namespace std;

// Returns the count of ways we can
// sum coins[0...n-1] coins to get sum "sum"
int countRecur(vector<int>& coins, int n, int sum, 
               vector<vector<int>> &memo) {
  
    // If sum is 0 then there is 1 solution
    // (do not include any coin)
    if (sum == 0) return 1;

    // 0 ways in the following two cases
    if (sum < 0 || n == 0) return 0;
    
    // If the subproblem is previously calculated then
    // simply return the result
    if (memo[n-1][sum]!=-1) return memo[n-1][sum];

    // count is sum of solutions (i)
    // including coins[n-1] (ii) excluding coins[n-1]
    return memo[n-1][sum] = 
        countRecur(coins, n, sum - coins[n-1], memo) + 
        countRecur(coins, n - 1, sum, memo);
}

int count(vector<int> &coins, int sum) {
    
    vector<vector<int>> memo(coins.size(), vector<int>(sum+1, -1));
    return countRecur(coins, coins.size(), sum, memo);
}

int main() {
    vector<int> coins = {1, 2, 3};
    int sum = 5;
    cout << count(coins, sum);
    return 0;
}
Java
// Java program for coin change problem.
// using memoization
import java.util.Arrays;

class GfG {

    static int countRecur(int[] coins, int n, int sum, int[][] memo) {

        // If sum is 0 then there is 1 solution
        // (do not include any coin)
        if (sum == 0) return 1;

        // 0 ways in the following two cases
        if (sum < 0 || n == 0) return 0;

        // If the subproblem is previously calculated then
        // simply return the result
        if (memo[n - 1][sum] != -1) return memo[n - 1][sum];

        // count is sum of solutions (i)
        // including coins[n-1] (ii) excluding coins[n-1]
        return memo[n - 1][sum] = 
                countRecur(coins, n, sum - coins[n - 1], memo) + 
                countRecur(coins, n - 1, sum, memo);
    }

    static int count(int[] coins, int sum) {
        int[][] memo = new int[coins.length][sum + 1];
        for (int[] row : memo) {
            Arrays.fill(row, -1);
        }
        return countRecur(coins, coins.length, sum, memo);
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 3};
        int sum = 5;
        System.out.println(count(coins, sum));
    }
}
Python
# Python program for coin change problem.
# using memoization
def countRecur(coins, n, sum, memo):
  
    # If sum is 0 then there is 1 solution
    # (do not include any coin)
    if sum == 0:
        return 1

    # 0 ways in the following two cases
    if sum < 0 or n == 0:
        return 0

    # If the subproblem is previously calculated then
    # simply return the result
    if memo[n - 1][sum] != -1:
        return memo[n - 1][sum]

    # count is sum of solutions (i)
    # including coins[n-1] (ii) excluding coins[n-1]
    memo[n - 1][sum] = (countRecur(coins, n, sum - coins[n - 1], memo) + 
                        countRecur(coins, n - 1, sum, memo))
    return memo[n - 1][sum]

def count(coins, sum):
    memo = [[-1 for _ in range(sum + 1)] for _ in range(len(coins))]
    return countRecur(coins, len(coins), sum, memo)

if __name__ == "__main__":
    coins = [1, 2, 3]
    sum = 5
    print(count(coins, sum))
C#
// C# program for coin change problem.
// using memoization

using System;

class GfG {

    static int countRecur(int[] coins, int n, int sum, int[,] memo) {

        // If sum is 0 then there is 1 solution
        // (do not include any coin)
        if (sum == 0) return 1;

        // 0 ways in the following two cases
        if (sum < 0 || n == 0) return 0;

        // If the subproblem is previously calculated then
        // simply return the result
        if (memo[n - 1, sum] != -1) return memo[n - 1, sum];

        // count is sum of solutions (i)
        // including coins[n-1] (ii) excluding coins[n-1]
        memo[n - 1, sum] = 
            countRecur(coins, n, sum - coins[n - 1], memo) + 
            countRecur(coins, n - 1, sum, memo);
        return memo[n - 1, sum];
    }

    static int count(int[] coins, int sum) {
        int[,] memo = new int[coins.Length, sum + 1];
        for (int i = 0; i < coins.Length; i++) {
            for (int j = 0; j <= sum; j++) {
                memo[i, j] = -1;
            }
        }
        return countRecur(coins, coins.Length, sum, memo);
    }

    static void Main() {
        int[] coins = {1, 2, 3};
        int sum = 5;
        Console.WriteLine(count(coins, sum));
    }
}
JavaScript
// JavaScript program for coin change problem.
// using memoization

function countRecur(coins, n, sum, memo) {

    // If sum is 0 then there is 1 solution
    // (do not include any coin)
    if (sum === 0) return 1;

    // 0 ways in the following two cases
    if (sum < 0 || n === 0) return 0;

    // If the subproblem is previously calculated then
    // simply return the result
    if (memo[n - 1][sum] !== -1) return memo[n - 1][sum];

    // count is sum of solutions (i)
    // including coins[n-1] (ii) excluding coins[n-1]
    memo[n - 1][sum] = countRecur(coins, n, sum - coins[n - 1], memo) + 
                       countRecur(coins, n - 1, sum, memo);
    return memo[n - 1][sum];
}

function count(coins, sum) {
    const memo = Array.from({ length: coins.length }, () => Array(sum + 1).fill(-1));
    return countRecur(coins, coins.length, sum, memo);
}

const coins = [1, 2, 3];
const sum = 5;
console.log(count(coins, sum));

Output
5

Using Bottom-Up DP (Tabulation) – O(sum*n) Time and O(sum*n) Space

The idea is to fill the DP table based on previous values. For each coin, we either include it or exclude it to compute the minimum number of coins needed for each sum. The table is filled in an iterative manner from i = n-1 to i = 0 and for each sum from 1 to sum.

The dynamic programming relation is as follows: 

  • if (sum-coins[i]) is greater than 0, then dp[i][sum] = dp[i][sum-coins[i]] + dp[i+1][sum].
  • else dp[i][sum] = dp[i+1][sum].
C++
// C++ program for coin change problem using tabulation
#include <bits/stdc++.h>
using namespace std;

// Returns total distinct ways to make sum using n coins of
// different denominations
int count(vector<int>& coins, int sum) {
    int n = coins.size();
    
    // 2d dp array where n is the number of coin
    // denominations and sum is the target sum
    vector<vector<int> > dp(n + 1, vector<int>(sum + 1, 0));

    // Represents the base case where the target sum is 0,
    // and there is only one way to make change: by not
    // selecting any coin
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= sum; j++) {

            // Add the number of ways to make change without
            // using the current coin,
            dp[i][j] += dp[i - 1][j];

            if ((j - coins[i - 1]) >= 0) {

                // Add the number of ways to make change
                // using the current coin
                dp[i][j] += dp[i][j - coins[i - 1]];
            }
        }
    }
    return dp[n][sum];
}

int main() {
    vector<int> coins = {1, 2, 3};
    int sum = 5;
    cout << count(coins, sum);
    return 0;
}
Java
// Java program for coin change problem using tabulation

class GfG {

    static int count(int[] coins, int sum) {
        int n = coins.length;
        
        // 2d dp array where n is the number of coin
        // denominations and sum is the target sum
        int[][] dp = new int[n + 1][sum + 1];

        // Represents the base case where the target sum is 0,
        // and there is only one way to make change: by not
        // selecting any coin
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {

                // Add the number of ways to make change without
                // using the current coin
                dp[i][j] += dp[i - 1][j];

                if ((j - coins[i - 1]) >= 0) {

                    // Add the number of ways to make change
                    // using the current coin
                    dp[i][j] += dp[i][j - coins[i - 1]];
                }
            }
        }
        return dp[n][sum];
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 3};
        int sum = 5;
        System.out.println(count(coins, sum));
    }
}
Python
# Python program for coin change problem using tabulation

def count(coins, sum):
    n = len(coins)
    
    # 2d dp array where n is the number of coin
    # denominations and sum is the target sum
    dp = [[0] * (sum + 1) for _ in range(n + 1)]

    # Represents the base case where the target sum is 0,
    # and there is only one way to make change: by not
    # selecting any coin
    dp[0][0] = 1
    for i in range(1, n + 1):
        for j in range(sum + 1):

            # Add the number of ways to make change without
            # using the current coin
            dp[i][j] += dp[i - 1][j]

            if (j - coins[i - 1]) >= 0:

                # Add the number of ways to make change
                # using the current coin
                dp[i][j] += dp[i][j - coins[i - 1]]
                
    return dp[n][sum]

if __name__ == "__main__":
    coins = [1, 2, 3]
    sum = 5
    print(count(coins, sum))
C#
// C# program for coin change problem using tabulation

using System;

class GfG {

    static int count(int[] coins, int sum) {
        int n = coins.Length;
        
        // 2d dp array where n is the number of coin
        // denominations and sum is the target sum
        int[,] dp = new int[n + 1, sum + 1];

        // Represents the base case where the target sum is 0,
        // and there is only one way to make change: by not
        // selecting any coin
        dp[0, 0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {

                // Add the number of ways to make change without
                // using the current coin
                dp[i, j] += dp[i - 1, j];

                if ((j - coins[i - 1]) >= 0) {

                    // Add the number of ways to make change
                    // using the current coin
                    dp[i, j] += dp[i, j - coins[i - 1]];
                }
            }
        }
        return dp[n, sum];
    }

    static void Main() {
        int[] coins = {1, 2, 3};
        int sum = 5;
        Console.WriteLine(count(coins, sum));
    }
}
JavaScript
// JavaScript program for coin change problem using tabulation

function count(coins, sum) {
    const n = coins.length;
    
    // 2d dp array where n is the number of coin
    // denominations and sum is the target sum
    const dp = Array.from({ length: n + 1 }, () => Array(sum + 1).fill(0));

    // Represents the base case where the target sum is 0,
    // and there is only one way to make change: by not
    // selecting any coin
    dp[0][0] = 1;
    for (let i = 1; i <= n; i++) {
        for (let j = 0; j <= sum; j++) {

            // Add the number of ways to make change without
            // using the current coin
            dp[i][j] += dp[i - 1][j];

            if ((j - coins[i - 1]) >= 0) {

                // Add the number of ways to make change
                // using the current coin
                dp[i][j] += dp[i][j - coins[i - 1]];
            }
        }
    }
    return dp[n][sum];
}

const coins = [1, 2, 3];
const sum = 5;
console.log(count(coins, sum));

Output
5

Using Space Optimized DP – O(sum*n) Time and O(sum) Space

In previous approach of dynamic programming we have derive the relation between states as given below:

  • if (sum-coins[i]) is greater than 0, then dp[i][sum] = dp[i][sum-coins[i]] + dp[i+1][sum].
  • else dp[i][sum] = dp[i+1][sum].

If we observe that for calculating current dp[i][sum] state we only need previous row dp[i-1][sum] or current row dp[i][sum-coins[i]]. There is no need to store all the previous states just one previous state is used to compute result.

C++
// C++ program for coin change problem
// using space optimised dp
#include <bits/stdc++.h>
using namespace std;

// Returns total distinct ways to make sum using n coins of
// different denominations
int count(vector<int> &coins, int sum) {
    int n = coins.size();
    
    // dp[i] will be storing the number of solutions for
    // value i. We need sum+1 rows as the dp is
    // constructed in bottom up manner using the base case
    // (sum = 0)
    vector<int> dp(sum + 1);

    // Base case (If given value is 0)
    dp[0] = 1;

    // Pick all coins one by one and update the table[]
    // values after the index greater than or equal to the
    // value of the picked coin
    for (int i = 0; i < n; i++)
        for (int j = coins[i]; j <= sum; j++)
            dp[j] += dp[j - coins[i]];
    return dp[sum];
}

int main() {
    vector<int> coins = {1, 2, 3};
    int sum = 5;
    cout << count(coins, sum);
    return 0;
}
Java
// Java program for coin change problem.
// using space optimised dp

class GfG {

    static int count(int[] coins, int sum) {
        int n = coins.length;
        
        // dp[i] will be storing the number of solutions for
        // value i. We need sum+1 rows as the dp is
        // constructed in bottom up manner using the base case
        // (sum = 0)
        int[] dp = new int[sum + 1];

        // Base case (If given value is 0)
        dp[0] = 1;

        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to the
        // value of the picked coin
        for (int i = 0; i < n; i++)
            for (int j = coins[i]; j <= sum; j++)
                dp[j] += dp[j - coins[i]];
                
        return dp[sum];
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 3};
        int sum = 5;
        System.out.println(count(coins, sum));
    }
}
Python
# Python program for coin change problem.
# using space optimised dp

def count(coins, sum):
    n = len(coins)
    
    # dp[i] will be storing the number of solutions for
    # value i. We need sum+1 rows as the dp is
    # constructed in bottom up manner using the base case
    # (sum = 0)
    dp = [0] * (sum + 1)

    # Base case (If given value is 0)
    dp[0] = 1

    # Pick all coins one by one and update the table[]
    # values after the index greater than or equal to the
    # value of the picked coin
    for i in range(n):
        for j in range(coins[i], sum + 1):
            dp[j] += dp[j - coins[i]]
            
    return dp[sum]

if __name__ == "__main__":
    coins = [1, 2, 3]
    sum = 5
    print(count(coins, sum))
C#
// C# program for coin change problem.
// using space optimised dp

using System;

class GfG {

    static int count(int[] coins, int sum) {
        int n = coins.Length;
        
        // dp[i] will be storing the number of solutions for
        // value i. We need sum+1 rows as the dp is
        // constructed in bottom up manner using the base case
        // (sum = 0)
        int[] dp = new int[sum + 1];

        // Base case (If given value is 0)
        dp[0] = 1;

        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to the
        // value of the picked coin
        for (int i = 0; i < n; i++)
            for (int j = coins[i]; j <= sum; j++)
                dp[j] += dp[j - coins[i]];
                
        return dp[sum];
    }

    static void Main() {
        int[] coins = {1, 2, 3};
        int sum = 5;
        Console.WriteLine(count(coins, sum));
    }
}
JavaScript
// JavaScript program for coin change problem
// using space optimised dp

function count(coins, sum) {
    const n = coins.length;
    
    // dp[i] will be storing the number of solutions for
    // value i. We need sum+1 rows as the dp is
    // constructed in bottom up manner using the base case
    // (sum = 0)
    const dp = Array(sum + 1).fill(0);

    // Base case (If given value is 0)
    dp[0] = 1;

    // Pick all coins one by one and update the table[]
    // values after the index greater than or equal to the
    // value of the picked coin
    for (let i = 0; i < n; i++)
        for (let j = coins[i]; j <= sum; j++)
            dp[j] += dp[j - coins[i]];
            
    return dp[sum];
}

const coins = [1, 2, 3];
const sum = 5;
console.log(count(coins, sum));

Output
5

Related articles:



Next Article

Similar Reads

three90RightbarBannerImg
  翻译: