Knapsack problem

The knapsack problem asks for the most valuable set of items that fits in a backpack: each item has a weight and a value, the backpack holds a limited total weight, and each item is taken (1) or left (0), so it is also called the 0/1 knapsack. It is solved with a table over the items and the capacities. If each item can be taken any number of times, it is solved like coin change, where one array holds the answer for each amount; if items can be cut into pieces, taking the most value per kilogram first is enough.

Explanation

You pack a backpack for a hike, and it holds 6 kg. There are four items: item 1 weighs 3 kg and is worth 6, item 2 is 2 kg worth 3, item 3 is 4 kg worth 7, and item 4 is 5 kg worth 9.

Four items drawn as boxes as wide as their weights, 3, 2, 4 and 5 cells, labelled item 1: 3 kg, value 6; item 2: 2 kg, value 3; item 3: 4 kg, value 7; item 4: 5 kg, value 9; below them an empty backpack of 6 cells labelled 6 kg.

Fig. 1. Four items and a 6 kg backpack. A box is as wide as the item is heavy.

Each item is in or out, so there are 2 · 2 · 2 · 2 = 16 sets to check. Each new item doubles that number, and thirty items give over a billion sets, far more than a program checks in a second.

The natural rule is to pack the items with the most value per kilogram first. Item 1 gives the most, 6 / 3 = 2 per kilogram, so it goes in; of the rest, only item 2 fits the 3 kg left. Items 1 and 2 are worth 6 + 3 = 9, and 1 kg stays empty. Items 2 and 3 fill the backpack exactly and are worth 3 + 7 = 10.

Two 6 kg backpacks drawn as rows of six 1 kg cells: the red one, packed by most value per kilogram first, holds item 1 (3 kg) and item 2 (2 kg), worth 9, with 1 kg left empty in grey; the blue one holds item 2 (2 kg) and item 3 (4 kg), filling all 6 kg, worth 10.

Fig. 2. The rule leaves 1 kg empty; items 2 and 3 fill the backpack.

Whether an item is a good choice depends on what fits in the capacity it leaves, and we can't know that in advance. So try both, starting from the last item: take item 4, and the first 3 items get the 1 kg left; leave it, and the first 3 items get all 6 kg. Either way, what remains is the same question with one item fewer. It depends on two numbers: how many first items are still allowed, and the capacity left.

A tree drawn left to right from the question (4 items allowed, 6 kg left) through columns for 3, 2, 1 and 0 items allowed, 17 questions in all; each edge either leaves the next item, keeping the kg, or takes it, marked with the item number. A red path takes item 4 and leaves the rest, a blue path leaves items 4 and 3 and takes items 2 and 1, and both end at the same highlighted question: 0 items allowed, 1 kg left.

Fig. 3. Two different sets of choices end at the same question: no items allowed and 1 kg left.

Different choices can end at the same question: taking item 4 alone and taking items 1 and 2 both leave 1 kg with no items allowed. There are only as many questions as pairs of the two numbers. On four items that is 5 · 7 = 35, more than the 16 sets, so here nothing is saved. The saving comes with size: thirty items give over a billion sets but only 31 · 7 = 217 questions, so most questions are reached by many different choices. So each question is answered once and stored, as in dynamic programming.

Keep a table with one cell per question: one row for each number of first items allowed, from none to all four, and one column for each capacity from 0 to 6 kg. A cell holds the best value the first items of its row can pack into its capacity; with no items, it is 0. Each new row adds one item. Leaving it gives the cell above. Taking it gives the cell in the row above, as many columns to the left as the item weighs, plus the item's value. The cell gets the larger.

One number per capacity, as in coin change, isn't enough. It would fill 6 kg with item 1 plus the best for 3 kg, which is item 1 again: 6 + 6 = 12. The row says which items are still allowed.

A knapsack table with rows for no items, item 1, items 1–2, 1–3 and 1–4 and columns for capacities 0 to 6 kg, filled one cell at a time; each new cell is highlighted with the cell above it and the cell in the row above, the item's weight to the left, outlined, ending with 10 in the last cell.

No items yet: nothing can be packed, so every capacity gets 0.

1/19

Fig. 4. Step through the table with the buttons or the arrow keys. Watch 6 kg in row 1–3: taking item 3 reads 2 kg in the row above, and that is where 10 comes from.

The answer, 10, is the last cell. The last row also holds the best value for every capacity from 0 to 6 kg.

Implementation

Item i, from 1 to n, weighs w[i] and is worth v[i]. The backpack, called a knapsack in problem statements, holds W, and c is a capacity from 0 to W. The table is a vector of vectors dp. In the stage names of dynamic programming:

  1. State: dp[i][c] is the best value of a set of the first i items with total weight at most c.

  2. Base case: dp[0][c] = 0 for every c: no items, nothing to pack.

  3. Order: items i from 1 to n outside, capacities c from 0 to W inside.

  4. Transition: leave item i, dp[i][c] = dp[i - 1][c]; if w[i] <= c, take it, dp[i][c] = max(dp[i][c], dp[i - 1][c - w[i]] + v[i]).

  5. Answer: dp[n][W].

For item 3 and capacity 6, the transition gives dp[3][6] = max(dp[2][6], dp[2][2] + 7) = max(9, 10) = 10.

The input holds n and W, then one line per item with its weight and value. The program prints the best value and the items taken. For the 6 kg backpack the input is:

4 6
3 6
2 3
4 7
5 9
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int n, W;
    cin >> n >> W;
    vector<int> w(n + 1), v(n + 1);
    for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];

    // state: dp[i][c] = the best value from the first i items with capacity c
    // base case: dp[0][c] = 0, no items
    vector<vector<long long>> dp(n + 1, vector<long long>(W + 1, 0));

    // order: items one by one, every capacity
    for (int i = 1; i <= n; i++) {
        for (int c = 0; c <= W; c++) {
            // transition: leave item i
            dp[i][c] = dp[i - 1][c];
            // or take it, if it fits
            if (w[i] <= c) dp[i][c] = max(dp[i][c], dp[i - 1][c - w[i]] + v[i]);
        }
    }

    // answer
    cout << dp[n][W] << "\n";

    // restore the items; remove this block if you only need the value
    vector<int> taken;
    for (int i = n, c = W; i >= 1; i--) {
        if (dp[i][c] != dp[i - 1][c]) {   // item i was taken
            taken.push_back(i);
            c -= w[i];
        }
    }
    reverse(taken.begin(), taken.end());
    for (int i : taken) cout << i << " ";
    cout << "\n";
}

The time and the memory are both O(n \cdot W) (see time complexity). They grow with W: with 100 items and W up to 10^9, the table would have 10^{11} cells. The weights must be whole numbers, because they index columns; items of 1.5 kg and 2.5 kg need their weights doubled first.

Restoring the items

The restore block walks up from dp[n][W], one row per step, in all. If dp[i][c] equals dp[i - 1][c], a best set exists without item i, so the walk leaves it. If they differ, the best value needed item i: the walk takes it and moves to c - w[i].

The final knapsack table, rows for no items and items 1, 1–2, 1–3, 1–4, capacities 0 to 6; the walk back from 10 at row 1–4, 6 kg outlines the cells 10, 10, 3, 0 and 0, with the cells where items 3 and 2 are taken shaded blue and the cells where items 4 and 1 are left shaded grey.

Fig. 5. The walk takes items 3 and 2, the blue set of Fig. 2.

On a tie the walk leaves the item. With a 5 kg backpack, row 1–4 ties at 9 (step 18 of Fig. 4): the walk leaves item 4, then item 3, and finds items 1 and 2, also worth 9.

One array

With 1000 items and W = 10^5, the table has 10^8 cells, 400 MB even as int, over a usual 128–256 MB limit. Each row reads only the row above, so one row best, updated in place, is enough. The time stays O(n \cdot W), and the memory drops to :

    vector<long long> best(W + 1, 0);      // base case: no items
    for (int i = 1; i <= n; i++)
        for (int c = W; c >= w[i]; c--)     // capacity downward
            best[c] = max(best[c], best[c - w[i]] + v[i]);
    cout << best[W] << "\n";                // answer

The loop runs downward because best[c - w[i]] lies to the left of best[c]. Going down, it isn't updated for item i yet, so it still holds the row above. Going up, it may already contain item i, and the line adds it again.

Two rows of seven cells for capacities 0 to 6, filled for item 1 of 3 kg worth 6: the top row runs from capacity 6 down to 3 and ends 0 0 0 6 6 6 6; the bottom row runs from 3 up to 6, where capacity 6 reads capacity 3 that already holds 6 and gets 12.

Going down, capacity 6 reads capacity 3, still 0: 0 + 6 = 6.

1/8

Fig. 6. Step through both directions. The outlined cell is the one being read, with its value at that moment.

One array can't restore the items, so keep the table when they are needed.

Exact weight

When the items must weigh exactly W, starting every cell at 0 is wrong: with items of 3 kg worth 6 and 2 kg worth 3, the downward loop sets best[4] to best[1] + 6 = 6, though no set of them weighs 4 kg. The 0 in best[1] meant "nothing packed in 1 kg", not "exactly 1 kg".

So start best[0] at 0, the empty set, and every other cell at a mark NEG that means no set weighs exactly c. Never add to a cell that holds NEG: NEG + v[i] is no longer NEG, and the check for "impossible" at the end would miss it.

    const long long NEG = -1e18;           // no set weighs exactly c
    vector<long long> best(W + 1, NEG);
    best[0] = 0;                            // base case: the empty set
    for (int i = 1; i <= n; i++)
        for (int c = W; c >= w[i]; c--)
            if (best[c - w[i]] != NEG)
                best[c] = max(best[c], best[c - w[i]] + v[i]);
    if (best[W] == NEG) cout << "impossible\n";
    else cout << best[W] << "\n";

With the two items above, best[4] reads best[1] for item 1 and best[2] for item 2, and both are still NEG when read. So best[4] stays NEG: no set weighs exactly 4 kg.

Correctness proof

The table keeps an invariant: d_{i,c}, the number in dp[i][c], is the largest value of a set of the first items with total weight at most .

For i = 0 only the empty set exists, worth 0. Let row i - 1 be correct, and take a best set for items and capacity . Without item , it is a set of the first i - 1 items within , worth at most d_{i-1,c}. With item , the rest fits in c - w_i, so the set is worth at most d_{i-1,c-w_i} + v_i. The transition takes the larger of the two, and both are values of real sets that fit, so d_{i,c} is the best. Taking reads row i - 1, so no item is counted twice.

Common mistakes

  • Packing the most value per kilogram first. On the 6 kg backpack it packs 9, not 10 (Fig. 2); that rule works only when items can be cut.

  • Swapping the loops in the one-array version, capacities outside. Then best[6] is finished before any smaller cell is filled, so it holds at most one item: the 6 kg backpack gets 9, item 4 alone. Keep the items outside.

Similar algorithms

Greedy algorithms solve the fractional knapsack, where items can be cut: the last item is cut to fit.

When W is up to 10^9, the table is too big. With at most about 20 items, try all 2^{20} sets with bitmasks or backtracking; with up to about 40, use meet in the middle.

Practice

Was this page helpful?