518. Coin Change II

練習日期:2026-02-23

難度:Medium

類型:Array、Dynamic Programming

📘 題目敘述

給你一個整數 amount,以及一個整數陣列 coins,表示不同面額的硬幣。

請回傳可以湊出總金額 amount 的「組合數量」。

如果無法湊出該金額,回傳 0

你可以假設每種硬幣的數量是無限的。

答案保證可以放進 32-bit 有號整數。

條件限制

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • 0 <= amount <= 5000

🧠 解題思路

這題是典型的「完全背包」組合數 DP。

我用一維 DP step[j] 表示:

湊出金額 j 的組合數量(只使用目前處理到的那些硬幣種類)。

初始化 step[0] = 1,表示湊出 0 元只有一種方式:什麼都不選。

接著我用外層迴圈跑每一種硬幣 i,內層從 j = i 跑到 amount

step[j] += step[j - i]

意思是:如果我要湊出 j,而且可以用面額 i,那我可以從「先湊出 j - i」的每一種方式後面再加一個 i,就會得到新的方式。

外層先跑硬幣、內層金額由小到大,這個順序能保證我們數到的是「組合」而不是「排列」(不會因為硬幣順序不同而重複計數)。

我用 long longstep 是避免中間加總溢位,並且如果超過 INT_MAX 就直接 cap 到 INT_MAX,讓回傳值仍符合題目 32-bit 的保證。

所有變數

  • step:DP 陣列,step[j] 表示湊出金額 j 的組合數量
  • i:目前處理的硬幣面額
  • j:目前要更新的金額

🪜 主要流程步驟

1. 建立 DP 陣列 step,並初始化 step[0] = 1

我用 step 長度 amount + 1,一開始全部設 0。

step[0] = 1,代表湊 0 元只有一種方式。


2. 外層跑硬幣種類,內層跑金額(由小到大)

對每個硬幣面額 i

我讓 ji 走到 amount

  • step[j] += step[j - i]

代表把所有「湊出 j-i 的方法」延伸成「湊出 j 的方法」。


3. 用 INT_MAX 封頂,避免 long long 結果太大

題目說答案一定在 32-bit 內,但中間加總我還是用 long long 存。

如果 step[j] > INT_MAX,我就把它設成 INT_MAX,讓回傳值不會爆掉。


4. 回傳 step[amount]

最後 step[amount] 就是湊出 amount 的組合數量。

⏱️ 複雜度

  • 時間複雜度:O(amount * coins.length) 每個硬幣都要更新一次 0..amount 的 DP。
  • 空間複雜度:O(amount) 只用一個長度 amount + 1 的一維 DP。

💻 程式碼實作

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<long long> step(amount + 1, 0);
        step[0] = 1;
        for (int i : coins) {
            for (int j = i; j <= amount; j++) {
                step[j] += step[j - i];
                if (step[j] > INT_MAX) {
                    step[j] = INT_MAX;
                }
            }
        }
        return step[amount];
    }
};

https://leetcode.com/problems/coin-change-ii/

作者: scottnick
撰寫日期: 2026-02-23
類別:
原文連結: https://scottnick.github.io/cpp-notes/all%20problems/518-coin-change-ii.html