coins each coin's candidate for the current amount: 1 + dp[a − c]
dp dp[a] = fewest coins that make a · ∞ = amount + 1, the "not reachable" sentinel
public int coinChange(int[] coins, int amount) { int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1); dp[0] = 0; for (int a = 1; a <= amount; a++) { for (int c : coins) { if (a - c >= 0) { dp[a] = Math.min(dp[a], 1 + dp[a - c]); } } } return dp[amount] != amount + 1 ? dp[amount] : -1;}
- a
- —
- c
- —
- 1 + dp[a − c]
- —
- dp[a]
- —
Why it works
- Brute force tries every coin at every step: recursing on
amount − cfor each coin explores a tree aboutcoinsamountwide. Greedy (biggest coin first) is fast but wrong: for[1,3,4]and 6 it picks 4 + 1 + 1, while 3 + 3 uses two coins. - The last coin decides: an optimal pile for
aends with some coinc, and the rest is an optimal pile fora − c. Sodp[a] = 1 + min over coins of dp[a − c]. Each arc into a cell is one coin's candidate. - Fill amounts upward: every
a − cis smaller thana, so it is final when read.amountcells timescoins.lengthchecks: O(amount · coins) time, O(amount) space. - Pitfall: the sentinel.
amount + 1works as infinity because no answer can use more thanamountcoins (the smallest coin is 1). Do not useInteger.MAX_VALUEinstead:1 + dp[a − c]then overflows to a negative number that wins theMath.min. An unreachabledp[amount]must turn into-1. - Which coins? Walk back from
amount: any coin withdp[a − c] = dp[a] − 1was a valid last coin. The banner lists one such pile. - Related: Coin Change II counts the combinations instead (loop coins outside, amounts inside). This is an unbounded knapsack; BFS over amounts gives the same answer.