2Brute2Best
🔥0

Knapsack with Duplicate Items

Medium·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelDP — Knapsack, Unbounded (one row, capacity low → high)
Complexity:O(2^(N + W))→O(N · W)
Key Insight

Keep one row: dp[c] is the best value that fits in capacity c using the items seen so far. For each item, go over c from the item's weight up to the capacity, low → high. When we reach c, the cell dp[c − w] was already updated for this item, so it may already hold a copy of it. That is what lets the item repeat.

Core Validity Invariant

After each pass, dp[c] is the best value that fits in capacity c using the items so far, and this holds because dp[c − w] may already include a copy of the current item, which is exactly the repeat we want.

01

Problem

You have a bag that can hold a total weight of capacity. You also have n kinds of items. Item i has a weight wt[i] and a value val[i].

You may take the same kind of item as many times as you like. Find the biggest total value you can pack without going over the capacity.

The function returns that biggest total value. It does not need to say which items were taken.

Input

Two lists val and wt of the same length n (the value and weight of each kind of item), and the integer capacity.

Output

One number: the largest total value of a set of items (repeats allowed) whose total weight is at most capacity.

Example 1
Input: val = [5, 7], wt = [2, 3], capacity = 7
Output: 17

Take item A twice (weight 4, value 10) and item B once (weight 3, value 7). The weight is 7 and the value is 17. No other mix that fits is worth more.

Example 2
Input: val = [10, 40, 50, 70], wt = [1, 3, 4, 5], capacity = 8
Output: 110

Take the item with weight 3 (value 40) and the item with weight 5 (value 70). The weight is 8 and the value is 110.

Example 3
Input: val = [1, 1], wt = [2, 1], capacity = 3
Output: 3

Take the weight-1 item three times: weight 3, value 3.

Constraints
  • • 1 ≤ capacity, val.size() ≤ 1000
  • • 1 ≤ val[i], wt[i] ≤ 100
  • • wt.size() = val.size()
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 kind of item more than once here? How does that change what a single decision looks like?

  2. 2.

    If I already know the best value for a smaller bag, how could it help me with a bigger bag?

  3. 3.

    For one capacity, which choices do I have for the item I am looking at right now?

  4. 4.

    If I fill the bag from the smallest capacity to the biggest, which cells already hold a new value when I read them?

  5. 5.

    How many (item, capacity) pairs are there, and how many calls would a recursion make for them?

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 ForceRecurse over the items: skip each one, or take one copy and stay on it. No memory.O(2^(N + W))O(N + W)
Betterdp[i][c] = best value using the first i items with room c; each cell is worked out onceO(N · W)O(N · W)
OptimalOne array dp[c]; for each item, go over c from its weight up to capacityO(N · W)O(W)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “you may take the same item as many times as you like”Items can repeat, so this is the unbounded knapsack. The capacity loop goes low → high.
  • “total weight is at most the capacity”The bag has room c, so the table is indexed by capacity.
  • “biggest total value”Keep the best of the choices: skip the item, or add one more copy of it.
Choose items with repeats allowed+total weight ≤ capacity+maximize total value→DP — Knapsack, Unbounded (one row, capacity low → high)

This is the anchor of the unbounded sub-pattern. The only change from 0/1 knapsack is the loop direction. 0/1 goes capacity high → low, so each item is read from the old row and can be used once. Unbounded goes low → high, so the item can be read from the row it is building. Its neighbours keep this idea, but change what a cell means (Coin Change) or the loop order (Coin Change II and Combination Sum IV).

10

Don't make this mistake

Pattern confusion: the 0/1 loop (capacity high → low)
✗ for (int c = capacity; c >= wt[i]; c--) dp[c] = max(dp[c], dp[c - wt[i]] + val[i]);
✓ for (int c = wt[i]; c <= capacity; c++) dp[c] = max(dp[c], dp[c - wt[i]] + val[i]);

High → low makes dp[c − w] still hold the value from before this item, so each item is used at most once. On the dry run (A = weight 2 value 5, B = weight 3 value 7, capacity 7), the high → low loop gives dp[7] = 12, which is A once plus B once. The correct answer is 17: A twice plus B once.

Forgetting the max with the old value
✗ dp[c] = dp[c - wt[i]] + val[i];
✓ dp[c] = max(dp[c], dp[c - wt[i]] + val[i]);

Overwriting throws away the best value found so far. On the dry run, item B at capacity 4 would replace dp[4] = 10 with 7, and the final answer would be 14 instead of 17.

Starting the inner loop at 0
✗ for (int c = 0; c <= capacity; c++) dp[c] = max(dp[c], dp[c - wt[i]] + val[i]);
✓ for (int c = wt[i]; c <= capacity; c++) dp[c] = max(dp[c], dp[c - wt[i]] + val[i]);

For any c smaller than the weight, c − wt[i] is negative, so the code reads before the start of the array. Start at the item's weight.

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.