Coin Change II
In the “Coin Change II” problem, we are given coins of various denominations and a specific amount. Our task is to determine the number of different ways we can make up that amount using the coins. Unlike other coin change problems where we need to minimize or maximize the number of coins, here we need to find the total number of possible combinations.
For instance, with coins [1,2,5] and amount 5, we have the following combinations:
- 5 = 5
- 5 = 2 + 2 + 1
- 5 = 2 + 1 + 1 + 1
- 5 = 1 + 1 + 1 + 1 + 1
Hence, there are 4 ways to make up the amount 5 using the coins [1,2,5].
Approach
The intuitive way to approach this problem is by leveraging dynamic programming. We can envision a solution where, for each coin, we update the number of ways to create every amount from 0 to the given amount.
Using a 1D Dynamic Programming Array
To find the solution in an optimized manner, we use a one-dimensional array, dp
, of size amount + 1
, where dp[i]
represents the number of ways to make up the amount i
using the coins considered so far.
- Initialize
dp
with all zeros and setdp[0]
to 1 since there’s only one way to make up amount 0, which is by not choosing any coin. - For each coin in
coins
, update the arraydp
from the coin value to the total amount. - For each amount
i
from the coin value to the total amount, incrementdp[i]
bydp[i - coin_value]
. - The final answer will be
dp[amount]
.
Example
Given the sample example with coins ([1,2,5]) and amount (5), let’s visualize how the dp
array changes with each iteration:
Initialization:
dp = [1, 0, 0, 0, 0, 0]
Processing coin (1):
- For each (j) from (1) to (5), add
dp[j - 1]
todp[j]
- After this step:
dp = [1, 1, 1, 1, 1, 1]
- For each (j) from (1) to (5), add
Processing coin (2):
- For each (j) from (2) to (5), add
dp[j - 2]
todp[j]
- After this step:
dp = [1, 1, 2, 2, 3, 3]
- For each (j) from (2) to (5), add
Processing coin (5):
- For (j = 5), add
dp[0]
todp[5]
- After this step:
dp = [1, 1, 2, 2, 3, 4]
- For (j = 5), add
Now, let’s visualize these iterations using a table.
Here’s a tabular representation of the algorithm’s progression for the given example:
Iteration | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
Initial | 1 | 0 | 0 | 0 | 0 | 0 |
Coin 1 | 1 | 1 | 1 | 1 | 1 | 1 |
Coin 2 | 1 | 1 | 2 | 2 | 3 | 3 |
Coin 5 | 1 | 1 | 2 | 2 | 3 | 4 |
- The “Initial” row indicates the state of the
dp
array right after initialization. - Each subsequent row represents the state of the
dp
array after processing the respective coin.
From the table, we can trace how the algorithm calculates the number of combinations for each amount using the available coins. The final value in the dp
array, dp[5]
, gives the total number of ways to make up the amount 5 using the coins [1,2,5], which is 4 in this case.
Rationale
The solution’s efficiency comes from its bottom-up construction. By building upon previously computed solutions for smaller amounts, we avoid redundant calculations. This method ensures that by the time we want to compute the number of ways for a particular amount, we’ve already determined the number of ways for all smaller amounts using the current set of coins.
Complexity
Time Complexity: O(amount×len(coins))O(\text{amount} \times \text{len(coins)})O(amount×len(coins))
Space Complexity: O(amount)O(\text{amount})O(amount)
This optimized solution ensures that we’re using both time and space resources efficiently.