2Brute2Best
🔥0

0/1 Knapsack

Medium·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelDP — Knapsack → 0/1 knapsack (one row)
Complexity:O(2^N)→O(N · W)
Key Insight

Each row only reads the row above it, so keep one row and update it in place. For each item, let c run from W down to wt, and set dp[c] = max(dp[c], val + dp[c − wt]). Because c goes down, dp[c − wt] is still the old row when it is read, so each item is counted at most once.

Core Validity Invariant

dp[c] is the best value for room c using each item at most once, and this holds because the loop goes c high → low: the cell it reads, dp[c − wt], is still the value from before this item.

01

Problem

You have a bag that can hold a total weight of at most W. There are n items. Item i has a value val[i] and a weight wt[i].

You may take each item at most once. Choose a set of items whose total weight is at most W, so that their total value is as large as possible.

Return that largest total value. The function is int knapsack(int W, vector<int>& val, vector<int>& wt).

Input

W, the weight limit. val and wt, two lists of the same length: the value and the weight of each item.

Output

A single number: the largest total value of a set of items whose total weight is at most W.

Example 1
Input: W = 4, val = [1, 2, 3], wt = [4, 5, 1]
Output: 3

Item 1 (weight 4) is worth 1 on its own. Item 2 weighs 5, which is over the limit. Item 3 (weight 1) is worth 3. Items 1 and 3 together weigh 5, which is too much, so the best is item 3 alone.

Example 2
Input: W = 50, val = [60, 100, 120], wt = [10, 20, 30]
Output: 220

Items 2 and 3 weigh 20 + 30 = 50, exactly the limit, and are worth 100 + 120 = 220. Adding item 1 would push the weight past 50.

Example 3
Input: W = 3, val = [1, 2, 3], wt = [4, 5, 6]
Output: 0

Every item weighs more than 3, so nothing fits.

Constraints
  • • 1 ≤ W ≤ 10^3
  • • 1 ≤ n ≤ 10^3, and val and wt have the same length
  • • 1 ≤ val[i] ≤ 10^3
  • • 1 ≤ wt[i] ≤ 10^3
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Can I take the same item twice here? What in the statement tells me?

  2. 2.

    For the first item, I either take it or I do not. What is left for the other items in each case?

  3. 3.

    The room left in the bag shrinks as I take items. What should I remember about each possible room size?

  4. 4.

    If I always take the item with the best value for its weight first, will I always get the best total? Try a small example.

  5. 5.

    How many (number of items considered, room left) pairs are there, and could I fill them in one by one?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFor each item, try skipping it and taking it; keep the best totalO(2^N)O(N)
BetterFill dp[i][c] once for every (item count, room) pair; each cell is O(1)O(N · W)O(N · W)
OptimalKeep one row dp[c]; for each item, update it with c going from W down to the item's weightO(N · W)O(W)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “each item at most once”Each item is either in or out, and it can be used only once → 0/1 knapsack. The capacity loop runs high → low.
  • “total weight at most W”The room left in the bag is the state. dp[c] stores the best value for each room c.
  • “largest total value”Every cell is a max of two choices: skip the item or take it.
Choose items with total weight ≤ W+each item at most once+maximise total value→DP — Knapsack → 0/1 knapsack (one row)

This is the anchor of the Knapsack pattern. The only thing that makes it 0/1 is the loop direction: going high → low means an item cannot feed itself. Subset Sum is this same row with true/false instead of values, and Ones and Zeroes adds a second capacity under the same rule.

10

Don't make this mistake

Pattern confusion: looping c low → high
✗ for (int c = wt[i]; c <= W; c++) dp[c] = max(dp[c], val[i] + dp[c - wt[i]]);
✓ for (int c = W; c >= wt[i]; c--) dp[c] = max(dp[c], val[i] + dp[c - wt[i]]);

Going up reads dp[c − wt] after it was already updated for this same item, so the item adds its value again. On W = 4, val = [1, 2, 3], wt = [4, 5, 1], item 3 (w 1, v 3) becomes dp = 0, 3, 6, 9, 12. The answer 12 uses the weight-1 item four times. The correct answer is 3.

Greedy: take the best value-per-weight first
✗ sort by val / wt, then take each item that still fits
✓ fill the dp row (it considers every subset)

Take W = 50 with items (w 10, v 60), (w 20, v 100) and (w 30, v 120). The ratios are 6, 5 and 4. Greedy takes the first two (weight 30, value 160), and then item 3 does not fit into the 20 left. Items 2 and 3 are worth 220. Greedy only works when you can take part of an item.

Forgetting the skip option
✗ dp[c] = val[i] + dp[c - wt[i]];
✓ dp[c] = max(dp[c], val[i] + dp[c - wt[i]]);

Overwriting drops the choice to skip. On W = 4, with items (w 1, v 3) and then (w 4, v 1), the answer is 3. The overwrite sets dp[4] = 1 + dp[0] = 1 when the second item is processed, which throws away the 3.

Missing the size check
✗ for (int c = W; c >= 0; c--) dp[c] = max(dp[c], val[i] + dp[c - wt[i]]);
✓ for (int c = W; c >= wt[i]; c--) dp[c] = max(dp[c], val[i] + dp[c - wt[i]]);

When c < wt[i], the index c − wt[i] is negative, so the read goes outside the array. With W = 4 and an item of weight 5, the loop reads dp[−1].

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like DP — Knapsack translate into production architectures and low-level memory mechanics.