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 with just coins of 2.
Example 3:
Input: amount = 10, coins = [10]
Output: 1
This is a classic unbounded knapsack problem where we can use each coin unlimited times. The key insight is that the order of coins doesn't matter (combinations, not permutations).
Key Insight:
dp[i] = number of ways to make amount iCreate a 1D DP array of size amount + 1:
dp[i] = number of combinations to make amount iInitialize base case:
dp[0] = 1 (one way to make 0: use no coins)For each coin:
i >= coin:
dp[i] += dp[i - coin]
Return dp[amount]
dp[i - coin] already includes all ways to use current coinThis problem demonstrates:
dp[0] = 1 is crucial1class Solution {
2 // Solution 1: Dynamic Programming (1D Array)
3 public int change(int amount, int[] coins) {
4 // dp[i] = number of ways to make amount i
5 int[] dp = new int[amount + 1];
6 dp[0] = 1; // One way to make 0: use no coins
7
8 // For each coin
9 for (int coin : coins) {
10 // Update all amounts that can be formed with this coin
11 for (int i = coin; i <= amount; i++) {
12 dp[i] += dp[i - coin];
13 }
14 }
15
16 return dp[amount];
17 }
18
19 // Solution 2: 2D DP (More explicit)
20 public int change2D(int amount, int[] coins) {
21 int n = coins.length;
22 // dp[i][j] = ways to make amount j using first i coins
23 int[][] dp = new int[n + 1][amount + 1];
24
25 // Base case: one way to make amount 0
26 for (int i = 0; i <= n; i++) {
27 dp[i][0] = 1;
28 }
29
30 // Fill the table
31 for (int i = 1; i <= n; i++) {
32 for (int j = 0; j <= amount; j++) {
33 // Don't use current coin
34 dp[i][j] = dp[i - 1][j];
35
36 // Use current coin if possible
37 if (j >= coins[i - 1]) {
38 dp[i][j] += dp[i][j - coins[i - 1]];
39 }
40 }
41 }
42
43 return dp[n][amount];
44 }
45
46 // Solution 3: Space-Optimized 2D DP
47 public int changeSpaceOptimized(int amount, int[] coins) {
48 int[] prev = new int[amount + 1];
49 int[] curr = new int[amount + 1];
50 prev[0] = 1;
51
52 for (int coin : coins) {
53 curr[0] = 1;
54 for (int j = 1; j <= amount; j++) {
55 curr[j] = prev[j];
56 if (j >= coin) {
57 curr[j] += curr[j - coin];
58 }
59 }
60 // Swap arrays
61 int[] temp = prev;
62 prev = curr;
63 curr = temp;
64 }
65
66 return prev[amount];
67 }
68}
69