0/1 knapsack
Choose which items to take, each at most once, to get the most value without going over a capacity.
O(n × capacity)Space O(n × capacity)8 practice problemsStep-through pictureThe idea
You have a bag that holds at most capacity units of weight, and a list of items. Each item has a weight and a value. You can take an item or leave it, but not take half of it or take it twice. That is why it is called 0/1. Which items give the most total value without going over the capacity?
Trying every combination means 2^n choices, about a million for 20 items. DP does better by asking smaller questions: "what is the best value using only the first i items with a bag of size cap?" Each answer comes from two smaller ones: skip item i, or take it.
The same table solves many problems that don't mention bags: can some numbers add up to exactly a target, can a list be split into two equal halves, how many subsets reach a total.
An everyday example
Packing a carry-on bag
You can bring a 7 kg carry-on and you have a laptop, a camera, books, and shoes, each with a weight and a value to you. For each item you ask one question: "is my best packing better with this item or without it?" Taking the item uses some weight, so you compare with the best packing of the other items in the space that is left.
| In the example | In the code |
|---|---|
| The bag's weight limit | capacity |
| Deciding about one item at a time | The outer loop over i |
| Best packing of the first items in a smaller bag | best[i][cap] |
| Leaving the item at home | best[i - 1][cap] |
| Packing it and filling the rest of the space well | best[i - 1][cap - weight] + value |
From the slow way to 0/1 knapsack
- Start with brute force: try every subset of items, skip any that go over
capacity, and keep the most valuable one. Withnitems there are2^nsubsets: 1,048,576 for 20 items, and about 1.1 trillion for 40. This is correct and far too slow. - Write the same search as a chain of choices. Look at the items one at a time, and for each one either skip it or take it. After deciding about the first
iitems, the only things that matter for the rest are how many items are decided and how much room is left in the bag. Which exact items you took no longer matters. - That changes the count of questions. Many different subsets end up at the same pair: "first
iitems decided,capunits of room". There are only(n + 1) × (capacity + 1)such pairs. For 40 items and a capacity of 1,000, that is 41,041 questions instead of a trillion subsets. - So build a table
best, wherebest[i][cap]is the most value you can get from the firstiitems in a bag of sizecap. Each cell compares skipping itemi, which reads the row above at the samecap, with taking it, which reads the row above atcap - weightand adds its value. Fill row by row. That loop is the 0/1 knapsack.
How it works
- Make a table
bestwithn + 1rows (no items, then items 1 ton) andcapacity + 1columns (bag sizes 0 tocapacity), all0. - Row 0 stays
0: with no items, the best value is nothing. - For each item
i, read itsweightandvalue. - For each bag size
cap, start with the value of skipping the item:best[i][cap] = best[i - 1][cap]. - If the item fits (
weight <= cap), compare with taking it:best[i - 1][cap - weight] + value, and keep the larger. - The answer is
best[n][capacity]: all items considered, full bag size.
Watch it run
This picture runs the code below on a real input. Press Step to move one step at a time, or Play to watch. The highlighted line is the one that just ran. Change the input to see other cases.
See every step at onceThe same run, laid out side by side
The code
Point at a note to light up the lines it explains.
function knapsack(weights, values, capacity) {const n = weights.length;const best = Array.from({ length: n + 1 }, () => new Array(capacity + 1).fill(0));for (let i = 1; i <= n; i++) {const weight = weights[i - 1];const value = values[i - 1];for (let cap = 0; cap <= capacity; cap++) {best[i][cap] = best[i - 1][cap];if (weight <= cap) {best[i][cap] = Math.max(best[i][cap], best[i - 1][cap - weight] + value);}}}return best[n][capacity];}
How fast it is
| Time | O(n × capacity) |
|---|---|
| Extra memory | O(n × capacity) |
One cell for every pair of item count and bag size, each filled with one comparison. The table can shrink to one row of capacity + 1 numbers if you loop the bag sizes from largest to smallest.
- Best case
O(n × capacity)Any input, even when every item is too heavy: each of the
n × (capacity + 1)cells is still visited once. - Average case
O(n × capacity)Any input: one copy and at most one comparison per cell.
- Worst case
O(n × capacity)The same. The weights and values never change the number of cells.
Common mistakes
- Reading
best[i][cap - weight](the same row) instead ofbest[i - 1][cap - weight]. That lets one item be taken many times. - Indexing items with
weights[i]while rows start at 1, which skips the first item and reads past the end. Rowiusesweights[i - 1]. - Sorting by value per weight and taking the best ratio first. That greedy rule is only right when items can be cut into pieces.
- Using this table when
capacityis huge, such as10^9. The table won't fit; look for a smaller dimension to index by.
When to use it
Look for these clues in a problem:
- Each item can be used at most once and has a cost (weight, size, price, time).
- There is a limit on the total cost, and you want the most value, or an exact total, or a count of ways.
- The question is about choosing a subset: "pick some of these so that...".
- The limit is a reasonably small whole number (up to a few thousand), so a table with one column per amount fits in memory.
Check yourself
Answer each one in your head first, then open it.
Why does the time depend on capacity and not just on n?
There is one cell per bag size, so a bigger capacity means a bigger table. This is called pseudo-polynomial: fast when the capacity number is small, slow when it is large, even with few items.
What changes if the taking case reads best[i][cap - weight] instead?
The rest of the bag could then already contain item i, so items can be taken many times. That turns it into the unbounded knapsack.
In the trace, why does item 4's row match item 3's row exactly?
At every size, taking item 4 is never better than what the first three items already give. At size 6 it ties at 14, and Math.max keeps the same number.
How many cells does the table have for 50 items and capacity 200?
51 × 201 = 10,251 cells, counting row 0 and bag size 0.
Practice problems
Stuck on one? Tuto is in the playground too, and gives you hints, never the answer.
0 of 8 solved. Solve one to earn the bronze medal, half for silver, and all of them for gold.
Start practicingmedium
Go further
You don't need these to start practicing. Open one when you want more.
Why it works
The reasoning that makes it always right
The table keeps one promise: once row i is filled, best[i][cap] is the most value from any subset of the first i items whose weights add up to at most cap. Row 0 is all zeros, which is right, because the only subset of no items is empty.
Filling row i keeps the promise. Take a best subset for (i, cap). If it leaves item i out, it is a valid subset of the first i - 1 items for the same cap, so its value is best[i - 1][cap]. If it uses item i, the other items in it are a subset of the first i - 1 items that fits in cap - weight, and they must be a best such subset; otherwise swapping in a better one would beat the best. So its value is best[i - 1][cap - weight] + value. The code computes both and keeps the larger.
Reading row i - 1 when taking the item is what enforces "at most once": the rest of the bag comes from items before i, so item i can't appear twice. Each row only reads the row above, so filling rows in order is safe. After the last row, the promise says best[n][capacity] is the answer.
Going deeper
4 more ideas
What each cell of the table means
best[i][cap] is the largest total value you can carry if you may only use the first i items and the bag holds cap units. Each row adds one more item to choose from. Each column is a different bag size. The bottom-right cell is the full question.
Why taking reads the row above
When you take item i, the rest of the bag must be filled from the earlier items only, because each item can be used once. That is row i - 1. Reading from row i instead would allow taking item i again, which is the unbounded knapsack.
How much work it does
There are (n + 1) * (capacity + 1) cells and each takes constant work, so the time is O(n * capacity). This is fast when the capacity is small. If the capacity is a billion, the table is too big, and other methods are needed.
Other questions on the same table
For "can some items add up to exactly target?", store true or false and use "or" instead of max. For "how many subsets add up to target?", store counts and add the two options. For a minimum, store Infinity for impossible cells and use Math.min.
Work it by hand
Every step in a table, with why
Pack a bag of capacity 6 from 4 items. Each row of the trace is one full row of the table, best[i], for bag sizes 0 to 6.
weights = [2, 1, 4, 3], values = [5, 3, 9, 6], capacity = 6
| Step | i | weight | value | best[i], sizes 0 to 6 | What happens | Why |
|---|---|---|---|---|---|---|
| 1 | 0 | - | - | 0, 0, 0, 0, 0, 0, 0 | No items yet | Row 0 means "no items to choose from", so every bag size is worth 0. |
| 2 | 1 | 2 | 5 | 0, 0, 5, 5, 5, 5, 5 | Fits from size 2 | Sizes 0 and 1 are too small and copy the row above. From size 2 up, taking the item gives 0 + 5. |
| 3 | 2 | 1 | 3 | 0, 3, 5, 8, 8, 8, 8 | Size 3 takes both | At size 3, taking item 2 adds 3 to best[1][2] = 5, which gives 8: both items fit together. |
| 4 | 3 | 4 | 9 | 0, 3, 5, 8, 9, 12, 14 | Sizes 4 to 6 improve | At size 6, taking it adds 9 to best[2][2] = 5, which gives 14. At size 4, it beats the old 8 by itself. |
| 5 | 4 | 3 | 6 | 0, 3, 5, 8, 9, 12, 14 | No cell changes | At size 6, taking it gives best[3][3] + 6 = 14, a tie with skipping. Math.max keeps 14, and line 14 returns best[4][6]. |
It returns 14. Two packings reach it: the items of weight 2 and 4 (5 + 9), or the items of weight 2, 1, and 3 (5 + 3 + 6). The loops end once the last item's row is filled.
Compared with other ways
When to pick something else
| Approach | Time | Extra memory | Choose it when |
|---|---|---|---|
| 0/1 knapsack | O(n × capacity) | O(n × capacity) | The capacity is a modest whole number, up to around a million cells in total, and you may need the chosen items. |
| One-row knapsack | O(n × capacity) | O(capacity) | Only the best value is needed, and memory is tight. |
| Try every subset | O(2^n) | O(n) | About 20 items or fewer, with a capacity too big for any table. |
| Greedy by value per weight | O(n log n) | O(n) | Only when items can be cut into fractions. For whole items it can miss the best packing. |
Extra memory: O(n × capacity) for the full table. Each row only reads the row above, so one row of capacity + 1 numbers is enough if bag sizes are looped from largest to smallest: O(capacity).
How the steps grow
- 0/1 knapsack, One-row knapsack
O(n × capacity) - Try every subset
O(2^n) - Greedy by value per weight
O(n log n)
| Approach | n = 10 | n = 1,000 | n = 1,000,000 |
|---|---|---|---|
| 0/1 knapsack | 100 | 1 million | 1 trillion |
| One-row knapsack | 100 | 1 million | 1 trillion |
| Try every subset | 1,024 | more than a quadrillion | more than a quadrillion |
| Greedy by value per weight | 33 | 9,966 | 20 million |
Inputs that need care
5 tricky inputs
capacity = 0- Every row has one cell, size 0. Only items of weight 0 fit, so the answer is the sum of their values, or 0 if there are none.
- An item heavier than the bag
weight <= capis never true for it, so its row is a copy of the row above. Nothing special is needed.- No items:
weights = [] - The table has only row 0, the loop never runs, and it returns 0.
- A weight that is not a whole number, such as
2.5 best[i - 1][cap - 2.5]reads a cell that doesn't exist, which isundefined, and the result turns intoNaN. Scale the weights to whole numbers first, for example by 10.- A huge capacity, such as
10^9 - Each row would need a billion cells, which won't fit in memory. Look for another small dimension: if the values are small, index the table by total value instead and store the least weight for each.
Variations and follow-ups
Twists interviewers add
Hit a target exactly
For "can some items add up to exactly target?", store true or false instead of values, start with only ok[0][0] true, and combine skip and take with "or". Splitting a list into two equal halves is this question with target set to half the total.
Count the subsets
Store counts instead of values and add skip and take instead of taking the larger. count[0][0] = 1, since the empty subset makes a total of 0 in one way.
List the chosen items
Start at best[n][capacity] and walk up. If best[i][cap] equals best[i - 1][cap], item i was skipped. Otherwise it was taken, so record it and move to cap - weight. This needs the full table, not the one-row version.
Many items, huge capacity
With up to about 40 items and any capacity, split the items into two halves, list every subset of each half (about a million each), sort one list, and match pairs. This meet-in-the-middle method takes about 2^(n/2) steps instead of 2^n.
In an interview
Spot it, explain it, answer follow-ups
- Spot it from "each item at most once", a limit (weight, budget, size), and "maximize" or "is it possible". Problems about splitting numbers into two groups or reaching a target sum are often this table in disguise.
- Describe the cell first: "
best[i][cap]is the best value from the firstiitems withcaproom". Then explain why taking reads rowi - 1: it stops an item from being used twice. - Test with a tiny case by hand, such as two items and capacity 3, and also check an item that doesn't fit and a capacity of 0.
- Expect "can you do it with one row?". Yes: loop
capfromcapacitydown toweight, sobest[cap - weight]still holds the old row's value when you read it. Looping upward would allow reuse.
Where frontend code uses it
The same idea in real UI code
- Choosing which images, fonts, or scripts to preload within a fixed byte budget, picking the most useful set, is a 0/1 knapsack.
- In a shopping cart, "use as much of a gift card as possible without going over" is the exact-total version on the item prices.
- Splitting a list of tasks into two sprints of equal effort, or two dashboard columns of equal height, is the subset-sum version.