Example 1
ready- Input
coins = [ 1, 2, 5 ] amount = 11- Expected
3- Why
- 5 + 5 + 1 reaches 11 with three coins
A checkout total doesn't care which coin you reach for first. It cares how few coins you can get away with using.
You're given a list coins, where each value is a denomination — the amount one kind
of coin is worth — and a non-negative integer amount. You have an unlimited supply of
every denomination. Return the fewest coins needed to total exactly amount.
A few ground rules:
-1 when no collection of the available coins reaches the amount.0 when amount is 0; choosing no coins already makes that total.1 <= coins.length <= 121 <= coins[i] <= 1_0000 <= amount <= 10_000Hints
Start at total 0. Adding one coin takes you to another reachable total. The first
time a level-by-level search reaches amount, it has used the fewest coins.
To make total a with a final coin coin, you first need the best answer for
a - coin. Try every denomination that fits.
Fill an array with a value larger than any possible answer, set dp[0] = 0, and
update dp[a] = min(dp[a], dp[a - coin] + 1). An untouched final entry means -1.
How would you return one actual minimum-length collection of coins, not just its size?
Visible cases
coins = [
1,
2,
5
]
amount = 113coins = [
2
]
amount = 3-1coins = [
1
]
amount = 00Interview signal
Breadth-first search (BFS) explores every position one step away before taking a
second step. Here a position is a reachable total, and one step means adding one coin.
Start from 0; the first time you reach amount, the BFS level is the minimum coin count.
from collections import deque
def coin_change(coins: list[int], amount: int) -> int:
queue = deque([(0, 0)])
seen = {0}
while queue:
total, used = queue.popleft()
if total == amount:
return used
for coin in coins:
next_total = total + coin
if next_total <= amount and next_total not in seen:
seen.add(next_total)
queue.append((next_total, used + 1))
return -1There are at most amount + 1 useful totals, and each one tries k = len(coins)
denominations. Time O(amount · k). Space O(amount) for the queue and visited set.
Dynamic programming (DP) records answers to smaller totals so a larger total can
reuse them. Let dp[a] be the fewest coins that make a. The base case is dp[0] = 0.
For every coin that fits, the transition — the rule for building a new DP answer — is:
dp[a] = min(dp[a], dp[a - coin] + 1)
def coin_change(coins: list[int], amount: int) -> int:
unreachable = amount + 1
dp = [unreachable] * (amount + 1)
dp[0] = 0
for current in range(1, amount + 1):
for coin in coins:
if coin <= current:
dp[current] = min(dp[current], dp[current - coin] + 1)
return -1 if dp[amount] == unreachable else dp[amount]amount + 1 is a safe marker because no valid minimum can use more than amount coins:
even that upper bound needs denomination 1. An entry that keeps the marker is
unreachable.
Time O(amount · k). Space O(amount) for the DP array.
Ready to run.
3 cases are queued.
Visible testcase
Case 1
Expected
3