題目在問什麼
給一組硬幣面額 coins,以及目標金額 amount。每種硬幣都可以用無限次,要求湊出 amount 所需的最少硬幣數;如果無法剛好湊到,就回傳 -1。
例子
Input: coins = [1, 2, 5], amount = 11
Output: 3
因為 11 = 5 + 5 + 1。
這題不能用貪心
這題很容易先想到貪心,但它不保證正確。
原因是「先拿大面額」不一定會導向最少硬幣數。某些面額組合下,最佳解可能根本不包含當前最大的硬幣。
所以這題更穩的方向是 Dynamic Programming。
解題思路
這份寫法是記憶化 DFS。
想法是把問題轉成:
- 要湊出
amount - 我可以先選任意一枚硬幣
coin - 然後把問題變成「湊出
amount - coin最少要幾枚」
對每個可能的 coin 都試一次,取最小值即可。
為了避免重複計算,把每個 amount 的結果存進 memo。之後如果再次遇到同樣的剩餘金額,就直接回傳,不用整棵遞迴樹重算。
代碼
public class Solution
{
public int CoinChange(int[] coins, int amount)
{
if (amount == 0) { return 0; }
Array.Sort(coins);
int[] memo = new int[amount];
return ClearCoinPathCounter(coins, amount, memo);
}
int ClearCoinPathCounter(int[] coins, int amount, int[] memo)
{
if (amount == 0) { return 0; }
if (memo[amount - 1] != 0) { return memo[amount - 1]; }
int minValue = int.MaxValue;
for (int i = 0; i < coins.Length; i++)
{
if (amount < coins[i]) { break; }
int stepCnt = ClearCoinPathCounter(coins, amount - coins[i], memo);
if (stepCnt >= 0 && stepCnt < minValue)
{
minValue = stepCnt + 1;
}
}
memo[amount - 1] = minValue == int.MaxValue ? -1 : minValue;
return memo[amount - 1];
}
}
思路圖

複雜度
記憶化之後,每個金額狀態最多只會真正算一次。
- 時間複雜度:
O(amount * coins.Length) - 空間複雜度:
O(amount),主要來自memo和遞迴堆疊
這題先記住的重點
- 這題不是穩定的貪心題
- 核心是把「最少硬幣數」拆成很多個子金額問題
- 記憶化是關鍵,不然 DFS 會重複算很多次