|
| 1 | +/* |
| 2 | +You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. |
| 3 | +
|
| 4 | +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. |
| 5 | +
|
| 6 | +You may assume that you have an infinite number of each kind of coin. |
| 7 | +
|
| 8 | +The answer is guaranteed to fit into a signed 32-bit integer. |
| 9 | +
|
| 10 | + |
| 11 | +
|
| 12 | +Example 1: |
| 13 | +
|
| 14 | +Input: amount = 5, coins = [1,2,5] |
| 15 | +Output: 4 |
| 16 | +Explanation: there are four ways to make up the amount: |
| 17 | +5=5 |
| 18 | +5=2+2+1 |
| 19 | +5=2+1+1+1 |
| 20 | +5=1+1+1+1+1 |
| 21 | +Example 2: |
| 22 | +
|
| 23 | +Input: amount = 3, coins = [2] |
| 24 | +Output: 0 |
| 25 | +Explanation: the amount of 3 cannot be made up just with coins of 2. |
| 26 | +Example 3: |
| 27 | +
|
| 28 | +Input: amount = 10, coins = [10] |
| 29 | +Output: 1 |
| 30 | + |
| 31 | +
|
| 32 | +Constraints: |
| 33 | +
|
| 34 | +1 <= coins.length <= 300 |
| 35 | +1 <= coins[i] <= 5000 |
| 36 | +All the values of coins are unique. |
| 37 | +0 <= amount <= 5000 |
| 38 | +*/ |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +// My Solution (fixed by GPT): |
| 43 | +class Solution { |
| 44 | + public int change(int amount, int[] coins) { |
| 45 | + int[] dp = new int[amount + 1]; |
| 46 | + dp[0] = 1; // 有 1 种方法凑出 0 元:什么也不选 |
| 47 | + |
| 48 | + for (int coin : coins) { |
| 49 | + for (int i = coin; i <= amount; i++) { |
| 50 | + dp[i] += dp[i - coin]; // 把当前硬币加入组合 |
| 51 | + } |
| 52 | + } |
| 53 | + return dp[amount]; |
| 54 | + } |
| 55 | +} |
| 56 | +/* |
| 57 | +Why must loop coins outside and loop amounts (dp) inside but not vice versa |
| 58 | +
|
| 59 | +本题的关键是「组合数」而不是「排列数」 |
| 60 | +用一个具体例子来说明: |
| 61 | +
|
| 62 | +金额=5,硬币=[1,2,5] |
| 63 | +跟踪dp数组的填充过程: |
| 64 | +
|
| 65 | +金额i=1: |
| 66 | + 遍历硬币1: dp[1] += dp[0] = 0 + 1 = 1 (使用一个1元硬币) |
| 67 | + 遍历硬币2: dp[1] += dp[-1] (不合法,跳过) |
| 68 | + 遍历硬币5: dp[1] += dp[-4] (不合法,跳过) |
| 69 | + 结果: dp[1] = 1 |
| 70 | +
|
| 71 | +金额i=2: |
| 72 | + 遍历硬币1: dp[2] += dp[1] = 0 + 1 = 1 (使用一个1元硬币) |
| 73 | + 遍历硬币2: dp[2] += dp[0] = 1 + 1 = 2 (使用一个2元硬币) |
| 74 | + 遍历硬币5: dp[2] += dp[-3] (不合法,跳过) |
| 75 | + 结果: dp[2] = 2 |
| 76 | +
|
| 77 | +金额i=3开始错误: |
| 78 | + 遍历硬币1: dp[3] += dp[2] = 0 + 2 = 2 (使用一个1元硬币 + 之前凑2元的方法) |
| 79 | + 遍历硬币2: dp[3] += dp[1] = 2 + 1 = 3 (使用一个2元硬币 + 之前凑1元的方法) |
| 80 | + 遍历硬币5: dp[3] += dp[-2] (不合法,跳过) |
| 81 | + 结果: dp[3] = 3 |
| 82 | +
|
| 83 | +分析dp[3]=3的组成: |
| 84 | +得到了3种方式,但实际上组合数应该是2种:{1,1,1}, {1,2} |
| 85 | +为什么多计算了1种?因为{1,2}和{2,1}被当作不同的排列计算了 |
| 86 | +
|
| 87 | +问题的根本原因: |
| 88 | +当先考虑金额,后考虑硬币时,在每个金额i处,都在重新考虑所有硬币 |
| 89 | +这意味着在不同顺序中可能多次使用同一种硬币 |
| 90 | +例如在计算dp[3]时,既考虑了"先用硬币1再用硬币2"的情况,也考虑了"先用硬币2再用硬币1"的情况 |
| 91 | +
|
| 92 | +而外循环为硬币时,是按照硬币种类的固定顺序来构建解决方案,这确保了相同的组合只被计算一次,不管其元素的顺序如何。 |
| 93 | +这就是为什么外循环为金额时计算的是排列数(考虑顺序),而题目要求的组合数(不考虑顺序)必须用外循环为硬币的方式来解决。 |
| 94 | +
|
| 95 | +而正确的解法(外层循环硬币)确保了每种硬币被考虑的顺序是固定的 |
| 96 | +
|
| 97 | +这样,对于每个硬币面值,按顺序考虑它可以如何构成各种金额。由于外层循环固定了考虑硬币的顺序,所以避免了重复计算组合。 |
| 98 | +简单来说: |
| 99 | +外层循环硬币:计算的是组合数(不考虑顺序) |
| 100 | +外层循环金额:计算的是排列数(考虑顺序) |
| 101 | +
|
| 102 | +题目要求组合数,所以必须把硬币循环放在外层。 |
| 103 | +*/ |
0 commit comments