The fewest coins from a limited supply
You hold counts[i] coins of value coins[i]. Return the fewest coins that pay exactly
amount, or −1 if it cannot be paid with the coins you hold.
Examples
| Input | Output | Why |
|---|---|---|
coins = [1,2,5], counts = [3,2,1], amount = 11 | 5 | 5 + 2 + 2 + 1 + 1. |
coins = [1,5], counts = [10,1], amount = 7 | 3 | 5 + 1 + 1. |
coins = [2], counts = [1], amount = 3 | -1 | Impossible. |
Constraints
- 1 ≤ coins.length ≤ 50
- 1 ≤ coins[i] ≤ 10⁴, distinct
- 1 ≤ counts[i] ≤ 1000
- 0 ≤ amount ≤ 10⁴
Hints
Hint 1
fewest[t] = the fewest coins paying exactly t, ∞ if impossible; fewest[0] = 0.
Hint 2
Split each supply into bundles 1, 2, 4, …, remainder; a bundle of b coins adds b coins and b · value.
Hint 3
Run the 0/1 update with min, totals downwards, over the bundles.
Where people lose marks
- Treating the supply as unlimited (the unbounded recurrence).
- Initialising with 0 instead of ∞ for totals not yet reachable.
- Bundles that exceed the supply.
Dry run on [1,2,5],[3,2,1],11
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 6fewest00∞1∞2∞3∞4∞5∞6∞7∞8∞9∞10∞11
Zero coins pay 0.
-
Step 2 / 6fewest0011∞2∞3∞4∞5∞6∞7∞8∞9∞10∞11
Bundle of 1 × 1: pays 1 with 1 coin.
-
Step 3 / 6fewest00112233∞4∞5∞6∞7∞8∞9∞10∞11
Bundle of 2 × 1: pays 2 with 2 coins.
-
Step 4 / 6fewest001112233445∞6∞7∞8∞9∞10∞11
Bundle of 1 × 2: pays 2 with 1 coin.
-
Step 5 / 6fewest0011122324354657∞8∞9∞10∞11
Bundle of 1 × 2: pays 2 with 1 coin.
-
Step 6 / 6fewest00111223241526273839410511
Bundle of 1 × 5: pays 5 with 1 coin. fewest[11] = 5.
How to think about it
A bounded knapsack with min and an exact total. fewest[t] is the smallest number of coins paying exactly t.
Rewrite each supply of k coins as bundles of 1, 2, 4, …, and a remainder, which together can express any count from 0 to
k. Each bundle of b coins is a 0/1 item that adds b · value to the total and b to the count. Because min is idempotent,
reaching the same number of coins through different bundle choices does no harm.
Complexity. Time O(amount · Σ log counts) · Space O(amount)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def min_coins_limited(coins, counts, amount):
INF = float('inf')
fewest = [0] + [INF] * amount
for c, k in zip(coins, counts):
size = 1
while k > 0:
b = min(size, k) # a bundle of b coins of value c
for t in range(amount, b * c - 1, -1): # 0/1: downwards
fewest[t] = min(fewest[t], fewest[t - b * c] + b)
k -= b
size *= 2
return -1 if fewest[amount] == INF else fewest[amount]#include <algorithm>
#include <climits>
#include <vector>
int solve(const std::vector<int>& coins, const std::vector<int>& counts, int amount) {
const int INF = INT_MAX / 2;
std::vector<int> fewest(amount + 1, INF);
fewest[0] = 0;
for (size_t i = 0; i < coins.size(); ++i)
for (int k = counts[i], size = 1; k > 0; size *= 2) {
int b = std::min(size, k);
long long w = (long long)b * coins[i]; // one bundle
for (long long t = amount; t >= w; --t) fewest[t] = std::min(fewest[t], fewest[t - w] + b);
k -= b;
}
return fewest[amount] >= INF ? -1 : fewest[amount];
}class Solution {
public int minCoinsLimited(int[] coins, int[] counts, int amount) {
final int INF = Integer.MAX_VALUE / 2;
int[] fewest = new int[amount + 1];
java.util.Arrays.fill(fewest, INF);
fewest[0] = 0;
for (int i = 0; i < coins.length; i++)
for (int k = counts[i], size = 1; k > 0; size *= 2) {
int b = Math.min(size, k);
long w = (long) b * coins[i]; // one bundle
for (long t = amount; t >= w; t--) fewest[(int) t] = Math.min(fewest[(int) t], fewest[(int) (t - w)] + b);
k -= b;
}
return fewest[amount] >= INF ? -1 : fewest[amount];
}
}function minCoinsLimited(coins, counts, amount) {
const fewest = [0, ...new Array(amount).fill(Infinity)];
for (let i = 0; i < coins.length; i++)
for (let k = counts[i], size = 1; k > 0; size *= 2) {
const b = Math.min(size, k), w = b * coins[i]; // one bundle
for (let t = amount; t >= w; t--) fewest[t] = Math.min(fewest[t], fewest[t - w] + b);
k -= b;
}
return fewest[amount] === Infinity ? -1 : fewest[amount];
}fn solve(coins: &[i64], counts: &[i64], amount: i64) -> i64 {
let n = amount as usize;
let inf = i64::MAX / 2;
let mut fewest = vec![inf; n + 1];
fewest[0] = 0;
for (i, &c) in coins.iter().enumerate() {
let (mut k, mut size) = (counts[i], 1i64);
while k > 0 {
let b = size.min(k);
let w = (b * c) as usize; // one bundle
if w <= n {
for t in (w..=n).rev() {
fewest[t] = fewest[t].min(fewest[t - w] + b);
}
}
k -= b;
size *= 2;
}
}
if fewest[n] >= inf { -1 } else { fewest[n] }
}import qualified Data.Map.Strict as M
solve :: [Int] -> [Int] -> Int -> Int
solve coins counts amount = maybe (-1) id (M.lookup amount (foldl add (M.singleton 0 0) bundles))
where
split k size | k <= 0 = [] | otherwise = let b = min size k in b : split (k - b) (2 * size)
bundles = [(b * c, b) | (c, k) <- zip coins counts, b <- split k 1]
-- a map from reachable total to the fewest coins; each bundle used at most once
add best (w, b) = M.unionWith min best (M.fromList [(t + w, n + b) | (t, n) <- M.toList best, t + w <= amount])
Variants to try
- Return how many of each coin to use.
- Count the ways instead (binary splitting overcounts: use the per-count method).
- Pay at least the amount with the fewest coins.
Next problems
Ways to pay with a limited supply
Count the combinations of coins, each available a limited number of times, that pay an amount. Binary splitting would overcount here, so use a sliding window sum.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem → Medium15 minCan a subset reach the target?
Decide whether some subset of positive integers adds up to exactly the target. A boolean array over totals, filled downwards for each number.
Open the problem →Write a solution and run it against the real test table.
Keep going
Ways to pay with a limited supply
Count the combinations of coins, each available a limited number of times, that pay an amount. Binary splitting would overcount here, so use a sliding window sum.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem → Medium15 minCan a subset reach the target?
Decide whether some subset of positive integers adds up to exactly the target. A boolean array over totals, filled downwards for each number.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.