Coin Change II

Updated on 14 May, 2025
Coin Change II header image

Problem Statement

This challenge involves determining the total number of unique combinations of coins that equal a specific monetary amount. You are provided with an integer array coins, where each element represents the denomination of a coin. You are also given an integer amount, which is the total value you need to achieve through various combinations of the coins provided. Importantly, it's assumed you have access to an infinite supply of each coin denomination.

The main goal here is to calculate the number of possible ways to reach the exact amount using any combination of the coins in the array. If no combination of the given coins can achieve the specified amount, the function should return 0. The result should always fit within a 32-bit signed integer.

Examples

Example 1

Input:

amount = 5, coins = [1,2,5]

Output:

4

Explanation:

there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2

Input:

amount = 3, coins = [2]

Output:

0

Explanation:

the amount of 3 cannot be made up just with coins of 2.

Example 3

Input:

amount = 10, coins = [10]

Output:

1

Constraints

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • All the values of coins are unique.
  • 0 <= amount <= 5000

Approach and Intuition

To solve this problem, one can utilize dynamic programming due to its ability to break down complex problems into simpler, overlapping subproblems. Here's a step-wise intuition and approach:

  1. Create a dynamic programming array (dp) where the index represents a monetary amount, and the value at each index represents the number of ways to create that amount using the available coins. Initialize the array with zeros, and set dp[0] = 1 since there's one way to create the amount of zero—that is using no coins (an important base case).

  2. Iterate over each coin in the coins array. For each coin, update the dp array:

    • Start from the value of the coin to the desired amount (inclusive). For each intermediate amount i, update dp[i] by adding the value of dp[i - current coin] to it. This addition reflects the number of ways to make the amount i given that current coin is used.
  3. To further elaborate step 2, suppose you are processing the coin of value coin.

    • For each amount from coin to amount, the number of new combinations that can include this coin is equal to the combinations that result in the amount amount - coin.
  4. The final value at dp[amount] after processing all coins will give you the total number of combinations to form amount with the provided denominations.

By following the above dynamic programming approach, each amount from 1 to amount considers contributions from all accessible coin operations, effectively building up the total count of combinations by leveraging calculated results of prior amounts. This method is efficient due to its polynomial time complexity relative to the number and value of coins and the maximum amount desired.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    int countWays(int total, vector<int>& denom) {
        int count = denom.size();
        vector<unsigned long long> ways(total + 1);
        ways[0] = 1;  // Base case initialization

        for (int idx = count - 1; idx >= 0; idx--) {
            for (int sum = denom[idx]; sum <= total; sum++) {
                ways[sum] += ways[sum - denom[idx]]; // Update the number of ways to form sum
            }
        }

        return ways[total] <= INT_MAX ? ways[total] : -1; // Handle overflow scenarios
    }
};

In the provided C++ solution to the "Coin Change II" problem, the primary objective is to compute the number of different ways to make up a specific amount (total) using given denominations (denom). The approach is implemented using dynamic programming, ensuring efficiency and handling potential edge cases such as integer overflows.

The code defines a method countWays, which receives two parameters: total, the amount for which change is to be counted, and denom, a vector of coin denominations available. The method uses a vector ways to store the number of ways change can be formed for every amount up to total. Initialization begins with setting ways[0] to 1 because there is exactly one way to make zero amount: using no coins.

The algorithm iterates backward through the coin denominations, updating the ways vector. For each denomination, it updates the ways for sums from the denomination value up to the total, by adding the number of ways to make the amount considering the current denomination. This accumulative addition ensures that all combinations using the current and previous coins are accounted for.

Finally, the method returns the total number of ways to make the specific amount, safeguarding against overflow with a conditional check, where if the result exceeds the maximum value an integer can hold (INT_MAX), it returns -1.

This implementation ensures:

  • Efficient computation using dynamic programming to avoid redundant calculations.
  • Handling of large number calculations to prevent overflow errors.
  • Simple and direct usage of the function with clear returns for both valid results and overflow scenarios.
java
class Solution {
    public int computeChange(int total, int[] denominations) {
        int length = denominations.length;
        long[] ways = new long[total + 1]; // Using long to handle large sums
        ways[0] = 1;

        for (int i = length - 1; i >= 0; i--) {
            for (int t = denominations[i]; t <= total; t++) {
                ways[t] += ways[t - denominations[i]];
            }
        }

        return ways[total] <= Integer.MAX_VALUE ? (int) ways[total] : -1; // Check limits and return
    }
}

The Java solution provided calculates the number of distinct ways to produce a given amount of money using a list of specified coin denominations. This approach is efficient due to its use of dynamic programming.

  • Begin by initializing an array ways of long integers to store the number of ways each amount from 0 to total can be achieved. There's special emphasis on using a long integer array to accommodate potentially large numbers.

  • Initialize the first element of the array ways[0] to 1. This signifies that there is one way to make the sum of 0, which is using no coins at all.

  • Process each coin denomination in reverse order using a nested loop structure:

    • The outer loop iterates over each coin denomination.

    • The inner loop updates the ways array for each amount that can be formed with the currently considered coin. It adds the number of ways to make the current amount minus the denomination, to the ways of the current amount.

  • After processing all coin denominations, check if the total number of ways to make the total amount is within the range of a standard integer. This is important as the number could potentially exceed the maximum value an integer can hold.

  • Finally, if the total number of ways fits within an integer's limits, cast it to an integer and return that value. Otherwise, for safety against integer overflow, return -1.

This manner of solution efficiently computes the solution without recursion, thereby avoiding common pitfalls such as stack overflow or excessive computations. Moreover, it verifies and handles edge cases like potential integer overflows gracefully, ensuring the robustness of the approach.

python
class Solution:
    def coinChange(self, total: int, denominations: List[int]) -> int:
        count = len(denominations)
        ways = [0] * (total + 1)
        ways[0] = 1

        for coin in range(count - 1, -1, -1):
            for val in range(denominations[coin], total + 1):
                ways[val] += ways[val - denominations[coin]]

        return ways[total]

This Python function tackles the problem of "Coin Change II" which aims to find the number of ways to make up a certain amount using a given set of coin denominations. Here’s an insight into solving this problem using dynamic programming:

  • Create an array ways initialized with zeros, with its size being total + 1. Set ways[0] = 1 to indicate that there is one way to make the total of 0, which is using no coins.

  • Loop through each coin denomination in reverse order. For each denomination, update the ways array; specifically, for each value from the denomination value to total, increment ways[val] by ways[val - denomination]. This update reflects that the number of ways to make up the amount val includes all the ways to make up val - denomination, using the current coin.

  • Finally, the function returns the value at ways[total], which provides the count of different ways to achieve the amount total using the denominations provided.

The loop structure ensures that combinations are built incrementally on top of each other, avoiding permutations of the same combinations but with the coins in different orders. This method efficiently computes the desired result using dynamic programming techniques.

Comments

No comments yet.