Przejdź do treści

1. 0/1 Knapsack

The 0/1 knapsack problem is one of the canonical examples of dynamic programming and one of the most widely studied NP-hard combinatorial optimisation problems. Given \(n\) items each with a value \(v_i\) and a weight \(w_i\), and a knapsack of capacity \(W\), it asks for the subset of items that maximises total value subject to the total weight not exceeding \(W\). Each item is either included (1) or excluded (0) — hence the name 12.

The problem is NP-hard in the strong sense when \(W\) is exponential in the input size; the dynamic programming solution runs in \(O(nW)\), which is pseudo-polynomial — polynomial in the value of \(W\) but not in its bit length \(\lceil \log_2 W \rceil\).

1.1 Problem Statement

Given \(n\) items with values \(v_1, \ldots, v_n \in \mathbb{Z}_{> 0}\), weights \(w_1, \ldots, w_n \in \mathbb{Z}_{> 0}\), and capacity \(W \in \mathbb{Z}_{> 0}\), find a binary vector \(x \in \{0, 1\}^n\) maximising:

\[ \begin{equation} \max \sum_{i=1}^{n} v_i x_i \quad \text{subject to} \quad \sum_{i=1}^{n} w_i x_i \leq W \label{eq:knapsack_opt} \end{equation} \]
Variables
Symbol Meaning
\(n\) Number of items
\(v_i\) Value of item \(i\)
\(w_i\) Weight of item \(i\)
\(W\) Knapsack capacity
\(x_i \in \{0,1\}\) Decision variable: 1 if item \(i\) is included
\(\text{dp}[i][c]\) Optimal value using items \(1..i\) with capacity \(c\)

1.2 Why Greedy Fails

A natural greedy heuristic — include items in decreasing order of value-per-unit-weight \(v_i / w_i\) — is optimal for the fractional knapsack (where items can be split) but not for the 0/1 version.

Counterexample: \(W = 10\), items: \((v_1, w_1) = (6, 4)\), \((v_2, w_2) = (5, 3)\), \((v_3, w_3) = (5, 3)\). Ratios: \(v_1/w_1 = 1.5\), \(v_2/w_2 = v_3/w_3 = 1.67\). Greedy picks items 2 and 3: total value = 10, weight = 6. But it cannot fit item 1 in a gap of \(W - 6 = 4 \geq w_1 = 4\) — wait, it can: optimal is items 1 and 2 (or 1 and 3): value = 11. Greedy (by ratio) would pick 2 and 3 then 1 if space allows, reaching 11. Let's use a cleaner counterexample: \(W = 5\), \((v_1, w_1) = (4, 3)\), \((v_2, w_2) = (3, 2)\), \((v_3, w_3) = (3, 2)\). Greedy by ratio: item 1 has ratio 1.33, items 2,3 have 1.5 — picks 2, then 3 (\(w=4 \leq 5\)), value=6. DP finds taking items 1+2 (\(w=5\), value=7) or 1+3 is also 7. Greedy beats ratio here actually... The correct intuition is: greedy works fractionally but fails 0/1 because it can leave "wasted" capacity.

1.3 Optimal Substructure and Recurrence

The 0/1 knapsack has optimal substructure: the optimal choice for items \(1..i\) with capacity \(c\) depends only on the optimal choice for items \(1..i-1\) with capacity \(c\) (exclude item \(i\)) or capacity \(c - w_i\) (include item \(i\), if \(w_i \leq c\)). This gives the recurrence:

\[ \begin{equation} \text{dp}[i][c] = \begin{cases} 0 & \text{if } i = 0 \text{ or } c = 0 \\ \text{dp}[i-1][c] & \text{if } w_i > c \\ \max\!\bigl(\text{dp}[i-1][c],\ v_i + \text{dp}[i-1][c - w_i]\bigr) & \text{if } w_i \leq c \end{cases} \label{eq:knapsack_dp} \end{equation} \]

1.4 The Algorithm

KNAPSACK(v, w, n, W):
  // fill dp table
  for i = 0 to n:
    dp[i][0] = 0
  for c = 0 to W:
    dp[0][c] = 0
  for i = 1 to n:
    for c = 1 to W:
      dp[i][c] = dp[i-1][c]          // exclude item i
      if w[i] ≤ c:
        dp[i][c] = max(dp[i][c], v[i] + dp[i-1][c - w[i]])  // include
  return dp[n][W]

RECONSTRUCT(dp, w, n, W):
  // backtrack to find which items were selected
  items = []
  c = W
  for i = n downto 1:
    if dp[i][c] ≠ dp[i-1][c]:
      items.append(i)
      c = c - w[i]
  return items

1.4.1 Space Optimisation

Only the previous row dp[i-1][·] is needed to compute row dp[i][·], so the table can be compressed to a 1D array of length \(W + 1\) by iterating \(c\) from \(W\) downto \(w_i\) (right-to-left):

KNAPSACK-1D(v, w, n, W):
  dp = array of size W+1, initialised to 0
  for i = 1 to n:
    for c = W downto w[i]:           // right-to-left prevents reuse
      dp[c] = max(dp[c], v[i] + dp[c - w[i]])
  return dp[W]

The right-to-left order ensures each item is not counted more than once (i.e. enforces the 0/1 constraint). Left-to-right would give the unbounded knapsack (each item may be selected multiple times).

1.5 Complexity

Aspect Cost
Time (DP table) \(O(nW)\)
Space (2D table) \(O(nW)\)
Space (1D optimised) \(O(W)\)
Reconstruction \(O(n)\) (with full table)

\(O(nW)\) is pseudo-polynomial: it is polynomial in \(n\) and the numeric value of \(W\), but exponential in the bit length of the input since \(W\) can be encoded in \(O(\log W)\) bits. No polynomial-time algorithm is known for the exact optimisation problem (NP-hardness follows from reduction from SUBSET-SUM) 12.

1.6 Fractional Knapsack (Comparison)

In the fractional knapsack problem items can be split: take any fraction \(x_i \in [0, 1]\) of item \(i\). The greedy algorithm (fill items by decreasing \(v_i / w_i\) ratio, taking fractions as needed) is optimal in \(O(n \log n)\) (dominated by sorting). The 0/1 version does not admit a greedy solution because fractional solutions have higher LP relaxation values.

Version Optimal algorithm Complexity
Fractional Greedy by ratio \(O(n \log n)\)
0/1 Dynamic programming \(O(nW)\) (pseudo-poly)
0/1 approximation FPTAS \(O(n^2 / \varepsilon)\) for \((1-\varepsilon)\)-approx

1.7 FPTAS

The 0/1 knapsack admits a fully polynomial-time approximation scheme (FPTAS): scale all values by \(K = \varepsilon v_{\max} / n\) (round down), solve the DP on the scaled values, and map back. The resulting solution has value \(\geq (1 - \varepsilon) \text{OPT}\) and runs in \(O(n^2 / \varepsilon)\) 2.

1.8 Interactive Demo

The canvas below shows the DP table being filled for a small instance (\(n = 6\) items, \(W = 15\)). Each cell \(\text{dp}[i][c]\) is coloured by value; the reconstructed path is highlighted after the table is complete.


  1. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms. MIT Press, 4th edition, 2022. ISBN 978-0-262-04630-5. ↩↩

  2. Jon Kleinberg and Éva Tardos. Algorithm Design. Addison-Wesley, 1st edition, 2006. ISBN 978-0-321-29535-4. ↩↩↩