Given:
coins[] — denominations (e.g. [1,2,5])amount — target value (e.g. 11)Return:
-1👉 Ask yourself:
Can I express this problem using smaller subproblems?
Define a function:
f(x) = minimum coins needed to make amount x
Then:
f(11) = min(
f(10) + 1,
f(9) + 1,
f(6) + 1
)
General form:
f(x) = min(f(x - coin)) + 1
function coinChange(coins: number[], amount: number): number {
function dfs(remain: number): number {
if (remain === 0) return 0;
if (remain < 0) return Infinity;
let min = Infinity;
for (const coin of coins) {
const res = dfs(remain - coin);
min = Math.min(min, res + 1);
}
return min;
}
const result = dfs(amount);
return result === Infinity ? -1 : result;
}
Time: O(k^amount)
Space: O(amount)
Cache results:
memo[x] = result of f(x)
function coinChange(coins: number[], amount: number): number {
const memo = new Map<number, number>();
function dfs(remain: number): number {
if (remain === 0) return 0;
if (remain < 0) return Infinity;
if (memo.has(remain)) {
return memo.get(remain)!;
}
let min = Infinity;
for (const coin of coins) {
const res = dfs(remain - coin);
min = Math.min(min, res + 1);
}
memo.set(remain, min);
return min;
}
const result = dfs(amount);
return result === Infinity ? -1 : result;
}
Time: O(amount × k)
Space: O(amount)
dp[i] = minimum coins needed for amount i
function coinChange(coins: number[], amount: number): number {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (const coin of coins) {
if (i - coin >= 0) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}
Time: O(amount × k)
Space: O(amount)
| Problem Type | Technique |
|---|---|
| Minimum / Maximum | DP |
| Count ways | DP |
| All combinations | Backtracking |