Skip to content
dsa450for five year olds

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

arrayprice of a packet
012342010450100
mappacket weight to price
1 kg202 kg103 kg44 kg505 kg100

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.

  1. f(0) = 0: an empty bag is free.
  2. For each available packet of weight s <= w, try cost[s - 1] + f(w - s), but only if f(w - s) is possible.
  3. f(w) is the cheapest of those, or "impossible" if none works.
tree
+1kg+1kg+1kg+1kg+1kg+2kg+2kg+3kg+2kg+3kg+4kg+2kg+3kg+4kg+5kgf(5)f(4)f(3)f(2)f(1)f(0)f(0)f(1)f(0)f(2)f(1)f(0)f(3)f(2)f(1)f(0)

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
java
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.

  1. dp[0] = 0. Every other box starts at "infinity", which means not possible yet.
  2. For each weight w from 1 to W, and each available packet size s <= w: if dp[w - s] is possible, try dp[w - s] + cost[s - 1].
  3. 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

gridcheapest cost to fill exactly w kg
0 kg1 kg2 kg3 kg4 kg5 kg0

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:

gridcheapest cost to fill exactly w kg
0 kg1 kg2 kg3 kg4 kg5 kg0infinf43inf

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
java
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 W does 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 from W.
  • What if W is huge, like a billion? Find the packet with the best price per kg, size b. A best answer uses fewer than b packets of other sizes (otherwise some of them add up to a multiple of b and can be swapped for cheaper b packets). So fill most of the bag with size b and 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.