Description
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.
Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.
You may assume that you have an infinite number of each kind of coin.
The answer is guaranteed to fit into a signed 32-bit integer.
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 <= 3001 <= coins[i] <= 5000- All the values of
coinsare unique. 0 <= amount <= 5000
Approach
- the loop is reversed wrt to 8. Coin Change because we are calculating combination instead of permutation
O(n*amount), O(amount)
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin: coins) {
for (int j = coin; j <= amount; j++) {
dp[j] += dp[j - coin];
}
}
return dp[amount];
}
}Primary Approach: 1D Space-Optimized Dynamic Programming ( Time, Space)
Intuition
To solve 518. Coin Change II, we use the Unbounded Knapsack formulation. Define dp[i] as the number of combinations to make up amount i:
- Base Case:
dp[0] = 1because there is exactly 1 way to make an amount of0(by choosing no coins). All otherdpentries start at0. - Outer Loop over Coins: Iterating through
coinsin the outer loop guarantees that coins are considered in a fixed order, counting combinations (where order does not matter, e.g.,[1, 2]is identical to[2, 1]). - Transition: For each coin and for each amount
ifromcointoamount:
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1; // 1 way to make amount 0 (using no coins)
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
Complexity
- Time Complexity: — Where is the number of coins and is the target value.
- Space Complexity: — 1D DP array of size .
Alternative Approach: 2D Unbounded Knapsack DP ( Time, Space)
Intuition
Define dp[i][j] as the number of ways to make amount j using a subset of the first i coins:
- Exclude Coin
i: Takedp[i - 1][j](ways to form amountjwithout using the current coin). - Include Coin
i: If , adddp[i][j - coins[i - 1]](since we can reuse the same coin infinitely, we look at rowirather than rowi - 1).
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = new int[n + 1][amount + 1];
// Base case: 1 way to make amount 0 with any prefix of coins
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
int coin = coins[i - 1];
for (int j = 1; j <= amount; j++) {
// Exclude current coin
dp[i][j] = dp[i - 1][j];
// Include current coin (unbounded reuse uses row i)
if (j >= coin) {
dp[i][j] += dp[i][j - coin];
}
}
}
return dp[n][amount];
}
}
Complexity
- Time Complexity: — Calculates transitions across all states.
- Space Complexity: — 2D table storing subproblem results.
Key Interview Discussion Points
- Combinations vs. Permutations:
- Outer loop =
coins, Inner loop =amount: Counts combinations (Coin Change II). Order does not matter, preventing duplicate groupings like1 + 2and2 + 1. - Outer loop =
amount, Inner loop =coins: Counts permutations (Combination Sum IV).
- Outer loop =
- 0-1 Knapsack vs. Unbounded Knapsack:
- In 0-1 Knapsack, inner loops run backward (
amountdown tocoin) to avoid using an item multiple times. - In Unbounded Knapsack, inner loops run forward (
coinup toamount), intentionally enabling repeated coin usage.
- In 0-1 Knapsack, inner loops run backward (
Easy Memory Rule
“Outer Loop = Coins Counts Combinations | Inner Loop Forward Infinite Supply!”