Dynamic Programming · 30 of 60
Minimum cost to fill given weight in a bag
Fill a bag to an exact weight with packets you can buy as often as you like, spending as little as possible.
- Simple way
- time O(n^W)
room O(W) - Clever way
- time O(n * W)
room O(W)
The problem, simply
You get a price list cost of length n and a bag size W in kilograms. cost[i] is the price of one packet that weighs i + 1 kg. A price of -1 means that packet size is sold out. The shop has as many packets of each size as you want.
Fill the bag to exactly W kg for the least money. If it cannot be done exactly, give back -1.
A story to remember it
Picture it
The price list. Box i sells a packet that weighs i + 1 kg. A 2 kg packet (10) plus a 3 kg packet (4) fills a 5 kg bag for 14.
- answer
Examples
A normal case
- In
cost = [20, 10, 4, 50, 100], W = 5- Out
14
A 2 kg packet (10) and a 3 kg packet (4). The 5 kg packet alone costs 100.
Cannot be done
- In
cost = [-1, -1, 4, 3, -1], W = 5- Out
-1
Only 3 kg and 4 kg packets exist. No mix of 3s and 4s adds up to 5.
Reusing a size
- In
cost = [-1, -1, 4, 3, -1], W = 6- Out
8
Two 3 kg packets. The same size can be bought more than once.
Fair prices
- In
cost = [1, 2, 3, 4, 5], W = 5- Out
5
Every kilogram costs 1 however you buy it, so any exact fill costs 5.
Way 1: the simple way
Try every packet as the last one in the bag, and solve the smaller bag that is left.
f(0) = 0: an empty bag is free.- For each available packet of weight
s <= w, trycost[s - 1] + f(w - s), but only iff(w - s)is possible. f(w)is the cheapest of those, or "impossible" if none works.
f(w) is the cheapest way to fill w kg. f(5) tries every packet first, and each choice leaves a smaller bag. Gray circles are bags we already priced on another branch. The plain recursion would redo all of their work: 32 calls for only 6 different bag sizes.
- seen
class Solution {
public int minimumCost(int[] cost, int W) {
if (W == 0) return 0; // empty bag is free
int best = Integer.MAX_VALUE;
for (int i = 0; i < cost.length; i++) {
int size = i + 1;
if (cost[i] == -1 || size > W) continue; // sold out or too heavy
int rest = minimumCost(cost, W - size);
if (rest != -1) best = Math.min(best, cost[i] + rest);
}
return best == Integer.MAX_VALUE ? -1 : best;
}
}How long it takes: up to n choices at each of up to W levels, O(n^W). Extra room: the waiting calls, O(W).
Way 2: the clever way
There are only W + 1 different bag sizes. Work out each one once, from light to heavy, in a row dp.
dp[0] = 0. Every other box starts at "infinity", which means not possible yet.- For each weight
wfrom 1 toW, and each available packet sizes <= w: ifdp[w - s]is possible, trydp[w - s] + cost[s - 1]. - The answer is
dp[W], or -1 if it is still infinity.
The arrows point from the lighter bags each box builds on.
Fill the cheat sheet from light bags to heavy bags
01 / 06
An empty bag costs nothing, so 0 kg gets 0. Now fill heavier bags one at a time.
- checking
- done
- answer
- just added
When some sizes are missing, some bags can never be filled:
Only 3 kg (price 4) and 4 kg (price 3) packets exist. Bags of 1, 2 and 5 kg can never be filled exactly, so they stay at infinity and the answer is -1.
- done
- rejected
import java.util.Arrays;
class Solution {
public int minimumCost(int[] cost, int W) {
final int INF = Integer.MAX_VALUE;
int[] dp = new int[W + 1];
Arrays.fill(dp, INF); // not possible yet
dp[0] = 0;
for (int w = 1; w <= W; w++) {
for (int i = 0; i < cost.length; i++) {
int size = i + 1;
if (cost[i] == -1 || size > w || dp[w - size] == INF) continue;
dp[w] = Math.min(dp[w], dp[w - size] + cost[i]); // last packet is this size
}
}
return dp[W] == INF ? -1 : dp[W];
}
}How long it takes: W weights times n packet sizes, O(n * W). Extra room: one row of W + 1 boxes, O(W).
Watch out for
- Sold out sizes are marked with -1. Do not treat -1 as a price of minus one.
- The bag must be filled exactly. A cheaper way that goes over
Wdoes not count. - This is the unbounded kind: one size can be used many times. That is why the loop over weights is on the outside and each box may reuse any packet.
What interviewers ask next
- What if each packet can be bought only once? Then it is 0/1 knapsack: loop over packets on the outside and weights from high to low.
- How many packets of each size? Keep a
choice[w]array of the size used for each box and walk back fromW. - What if
Wis huge, like a billion? Find the packet with the best price per kg, sizeb. A best answer uses fewer thanbpackets of other sizes (otherwise some of them add up to a multiple ofband can be swapped for cheaperbpackets). So fill most of the bag with sizeband run the small table only on the last few kilograms. - How is this like coin change? It is the same table: coins are packet sizes, and instead of counting coins you add their prices.
Remember this
Grown-up words: the cheat sheet is a 1D DP table, buying any packet as often as you like is the unbounded knapsack, "infinity" marks an unreachable state, and the repeated f(w) calls are overlapping subproblems.