Coin Change II
Problem
You are given an integer array coins representing coins of different denominations (e.g. 1 dollar, 5 dollars, etc) and an integer amount representing a target amount of money.
Return the number of distinct combinations that total up to amount. If it’s impossible to make up the amount, return 0.
You may assume that you have an unlimited number of each coin and that each value in coins is unique.
Examples
Example 1:
Input: amount = 4, coins = [1,2,3]
Output: 4
Explanation:
- 1+1+1+1 = 4
- 1+1+2 = 4
- 2+2 = 4
- 1+3 = 4
Example 2:
Input: amount = 7, coins = [2,4]
Output: 0
Constraints
1 <= coins.length <= 1001 <= coins[i] <= 50000 <= amount <= 5000
You should aim for a solution as good or better than O(n * a) time and O(n * a) space, where n is the number of coins and a is the given amount.
Solution
A regular DFS with memorization would loop over all possible next coins. But here we need to be careful about double counting permutations. So we make the invariant be the number of ways to make the remaining amount, but we can only pull from a subset of the coins starting from the start_idx. We then have 2 choices in the loop: use the current coin and recurse, or skip the coin.
class Solution {
public:
int change(int amount, vector<int>& coins) {
const int NUM_COINS = coins.size();
std::vector<std::vector<int>> memo(
NUM_COINS, std::vector<int>(amount + 1, -1)
);
// Number of ways to make remaining, only using coins
// from start_idx onwards
std::function<int(int, int)> dfs;
dfs = [&](int remaining, int start_idx) -> int {
// Successfully made the amount
if (remaining == 0) { return 1;}
// No coins left, or overshot the amount
if (start_idx == NUM_COINS || remaining < 0) { return 0; }
// Seen this result before
if (memo[start_idx][remaining] != -1) {
return memo[start_idx][remaining];
}
// Choise 1: use coins[start_idx], and stay at i because
// we can reuse that coin
int use = dfs(remaining - coins[start_idx], start_idx);
// Choice 2: skip coins[start_idx] and move to next coin
int skip = dfs(remaining, start_idx + 1);
memo[start_idx][remaining] = use + skip;
return memo[start_idx][remaining] ;
};
return dfs(amount, 0);
}
};