1.dp[x] = 湊出金額 x 最少需要幾枚硬幣
coins = [1,2,5];dp[0] = 0
dp[1] = 1 // 1
dp[2] = 1 // 2
dp[3] = 2 // 2+1
dp[4] = 2 // 2+2
dp[5] = 1 // 5
...
dp[11] = 3 // 5+5+1
現在金額 − 一枚 coin → 看前面最少用了幾枚,再 +1。
2.coins = [1,2]
dp[0] = 0
dp[1] = 1 // 1
算 dp[3] 時:
用 coin=1 → dp[2] + 1 = 1 + 1 = 2
用 coin=2 → dp[1] + 1 = 1 + 1 = 2
所以 dp[3] = 2不用重新研究「3 怎麼湊」;直接拿已經算好的 dp[2]、dp[1] 接一枚 coin。
3.把每個金額下「每種 coin 能不能當最後一枚」都比較一次,再留最少:
dp[currentAmount] = min(
dp[currentAmount],
dp[currentAmount - coin] + 1
);
4.dp[0] = 0;不能排除。 它是起點,例如:currentAmount = 5;coin = 5
dp[5 - 5] + 1= dp[0] + 1= 1 才能知道「5 元直接一枚 5 元硬幣」。
而:if (coin <= currentAmount)只是避免:currentAmount = 2;coin = 5;dp[2 - 5]= dp[-3]
最後那個「從最大硬幣開始,少就替換」是 Greedy(貪婪),這題不能保證正確,例如:
coins = [1,3,4], amount = 6;貪婪:4+1+1 = 3 枚;最佳:3+3 = 2 枚
記:不是從最大 coin 猜;是每個 amount 都問:「最後一枚如果是這個 coin,總共會幾枚?」然後取最小。
5.第一個 amount + 1:建立幾格。要有:dp[0] ~ dp[amount]所以總共要 amount + 1 格。
第二個 amount + 1:每一格一開始先填什麼值。先全部填成一個「不可能的值」,代表目前還沒找到答案。
例如:amount = 5就是:vector dp(6, 6);變成:dp = [6,6,6,6,6,6]
前面的 amount+1 = 格數;後面的 amount+1 = 初始值。
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {// 找湊到amount最少幾枚硬幣
vector<int> dp(amount + 1, amount + 1);//dp[x]=湊到x的最少硬幣數
dp[0] = 0;//0元不用任何硬幣
for (int currentAmount = 1; currentAmount <= amount; ++currentAmount) { //1 元一路算到amount
for (int coin : coins) {//嘗試每一種
if (coin <= currentAmount) {//這枚硬幣不能比目前金額大
dp[currentAmount] = min(
dp[currentAmount],//原本湊法
dp[currentAmount - coin] + 1//前面金額+現在這枚coin
);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];//沒辦法湊到就-1;回最少硬幣數
}
};