When you hear a problem that talks about a limited resource—weight, time, money, or slots—and asks for the best total benefit, you’re probably looking at a knapsack‑style question. The classic formulation is:

Given N items, each with a weight w_i and a value v_i, and a capacity C, choose a subset whose total weight ≤ C and whose total value is as large as possible.

In an interview you won’t see the exact textbook wording. Instead, you might read about a backpack, a budget, a schedule, or a memory limit. The pattern shows up in many disguises, but the core idea stays the same: a capacity constraint and an optimization objective.


1. Spotting the Knapsack Signal

SignalWhat it Means
"limited" or "maximum" capacityThere is a hard bound you cannot exceed.
"choose a subset" or "pick some items"You must decide which items to include, not how to order them.
"maximize/minimize total value/weight/profit"The objective is additive across chosen items.
"each item can be used at most once"0/1 knapsack; if repeats are allowed, it becomes the unbounded variant.
"items have two attributes (e.g., time and profit)"Typical knapsack dimensions: weight vs. value.

If you see at least two of these clues, the knapsack dynamic‑programming (DP) approach is worth considering.


2. The Core DP Idea

The DP builds a table where dp[c] (or dp[i][c]) stores the best value achievable with capacity c. For the 0/1 version you iterate over items and update capacities in reverse order to avoid reusing the same item.

2.1 Pseudocode

def knapsack_01(weights, values, capacity):
    n = len(weights)
    dp = [0] * (capacity + 1)
    for i in range(n):
        w, v = weights[i], values[i]
        for c in range(capacity, w - 1, -1):  # reverse to enforce 0/1
            dp[c] = max(dp[c], dp[c - w] + v)
    return dp[capacity]

The reverse loop guarantees each item contributes at most once. For the unbounded variant you loop forward, allowing unlimited reuse.


3. Complexity and Space Optimizations

  • Time – O(N · C) where N is the number of items and C the capacity. This is usually acceptable when C ≤ 10⁵; larger capacities may require alternative approaches (e.g., meet‑in‑the‑middle, greedy approximations).
  • Space – The 1‑D array above uses O(C) space. A 2‑D table dp[i][c] would be O(N · C) but is rarely needed unless you must reconstruct the chosen items.
  • Reconstruction – If you need the actual subset, keep a parallel boolean table or backtrack from the 2‑D version.

4. Common Pitfalls

  1. Off‑by‑one errors – Remember that capacities are inclusive; the array size is capacity + 1.
  2. Updating in the wrong direction – Forward updates turn a 0/1 problem into an unbounded one.
  3. Misreading the constraint – Sometimes the “capacity” is a budget in dollars, sometimes a time limit; treat it uniformly as an integer weight.
  4. Large capacities – If C is huge (e.g., 10⁹) but values are small, a value‑oriented DP (min weight for each value) can be more efficient.
  5. Duplicate items – Clarify whether each item appears once or many times; the loop direction changes accordingly.

5. Worked Example: "Project Scheduling with Limited Hours"

Problem statement (paraphrased)

You have 5 tasks. Each task i takes hours[i] hours and yields impact[i] points. You can work at most 8 hours total. Choose tasks to maximize total impact.

Step‑by‑step solution

  1. Identify the pattern – limited hours (capacity) and maximize impact → knapsack.
  2. Translate to weights/values:
    • weights = hours
    • values = impact
    • capacity = 8
  3. Apply the 0/1 DP code.
hours   = [2, 3, 4, 5, 1]
impact  = [3, 4, 5, 8, 2]
capacity = 8
print(knapsack_01(hours, impact, capacity))  # → 13

The optimal subset is tasks with hours [3,5] (impact 4+8=12) or [2,1,5] (impact 3+2+8=13). The DP returns 13, confirming the best achievable impact.


6. Five Practice Problems (No Copy‑Paste)

6.1 "Budgeted Marketing Campaign"

You have a list of advertising channels, each costing cost_i and expected to bring reach_i customers. With a total budget B, pick channels to maximize reach. Hint: Classic 0/1 knapsack. Use integer costs as weights.

6.2 "Memory‑Constrained Subset Sum"

Given an array of positive integers and a memory limit M (in bytes), each integer occupies 4 bytes. Choose a subset whose sum is as close as possible to a target T without exceeding M. Hint: Convert the memory limit to a capacity in terms of number of elements, then treat the target sum as the value to maximize.

6.3 "Unbounded Coin Change for Minimum Coins"

You have unlimited coins of denominations d₁…d_k. Find the fewest coins that sum to amount A. Hint: This is the unbounded knapsack where the weight is the coin value and the value is -1 (we minimize count). Use forward DP.

6.4 "Job Scheduling with Deadlines"

Each job has a profit p_i and a deadline d_i (in days). You can do at most one job per day. Maximize total profit. Hint: Sort jobs by profit descending, then greedily assign each to the latest free day before its deadline. This is a variant of the knapsack where capacity is the number of days.

6.5 "Resource Allocation for Cloud Instances"

You have N VM types, each requiring cpu_i cores and delivering throughput_i requests per second. With a total of C cores, maximize throughput. Hint: 0/1 knapsack if each type can be used once, or unbounded if you can spin up multiple identical VMs.


7. When to Reach for a Different Approach

  • If the capacity is tiny (≤ 30) but N is large, a bitmask exhaustive search may be faster.
  • If both N and C are large (≥ 10⁵), consider a greedy approximation or a meet‑in‑the‑middle technique.
  • When the objective is minimizing something under a lower bound (e.g., minimum weight to achieve at least value V), flip the DP orientation.

How to practice this

  1. Identify the constraint – For each practice problem, write down the capacity and what you’re optimizing before coding.
  2. Implement the 1‑D DP – Start with the template above, then adjust direction for unbounded vs. 0/1.
  3. Run a mock interview – Use Call Assistant to read the problem aloud, then answer in 60‑90 seconds while it captures your key points. Review the transcript to see if you mentioned the capacity signal and DP recurrence clearly.

FAQ

  • Q: How do I know if a problem needs a knapsack solution or a simple greedy algorithm? A: If the optimal choice depends on the combination of items (e.g., picking a heavy low‑value item may block a lighter high‑value one), greedy fails. Knapsack is needed when the decision is not locally optimal.

  • Q: Can I use recursion with memoization instead of an iterative DP? A: Yes, a top‑down memoized recursion works and is often easier to write, but it uses O(N·C) stack space and may be slower due to function‑call overhead.

  • Q: What if the capacity is not an integer (e.g., time in minutes with fractions)? A: Scale the values to eliminate fractions (e.g., multiply by 10) if the precision needed is modest, or switch to a value‑oriented DP if scaling would blow up the table size.

  • Q: How do I reconstruct the chosen items after the DP finishes? A: Keep a 2‑D table or store a predecessor pointer for each capacity update. Then backtrack from the final capacity, checking where the value changed.

Frequently asked questions

How do I know if a problem needs a knapsack solution or a simple greedy algorithm?

If the optimal set depends on the interaction between items—like a heavy low‑value item blocking a lighter high‑value one—greedy will miss the best combination. Knapsack is required when the decision is not locally optimal.

Can I use recursion with memoization instead of an iterative DP?

Yes, a top‑down memoized version works and is often easier to read, but it uses the same O(N·C) memory and can be slower because of recursion overhead.

What if the capacity is not an integer (e.g., time in minutes with fractions)?

Scale the numbers to make them integral (e.g., multiply by 10) if the required precision is modest. For very fine granularity, consider a value‑oriented DP that minimizes weight for each total value.

How do I reconstruct the chosen items after the DP finishes?

Store a predecessor pointer or keep a 2‑D table of decisions. Then backtrack from the final capacity, checking where the DP value changed to identify which items were taken.

#coding pattern#knapsack#dynamic programming#interview prep#algorithm