Solve and extend the Knapsack problem
Company: BitGo
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's understanding of dynamic programming for optimization problems, specifically the 0/1 knapsack formulation, space-optimized DP and the ability to reconstruct a chosen item set from compressed state.
Constraints
- 0 <= n <= 1000 (n == len(weights) == len(values))
- 0 <= W <= 10000
- 1 <= weights[i] <= W (when n > 0); 0 <= values[i] <= 10^6
- Each item may be used at most once (0/1 knapsack)
- If multiple optimal subsets exist, return the one from the standard backward reconstruction (indices sorted ascending)
Examples
Input: ([1, 3, 4, 5], [1, 4, 5, 7], 7)
Expected Output: (9, [1, 2])
Explanation: Items 1 (w=3,v=4) and 2 (w=4,v=5) fill weight 7 exactly for value 9 — better than item 3 alone (v=7) or items 0+3 (w=6,v=8).
Input: ([2, 3, 4, 5], [3, 4, 5, 6], 5)
Expected Output: (7, [0, 1])
Explanation: Items 0 (w=2,v=3) and 1 (w=3,v=4) use weight 5 for value 7, beating any single item that fits (max single value at w<=5 is 6).
Input: ([], [], 10)
Expected Output: (0, [])
Explanation: No items: max value is 0 and the chosen set is empty regardless of capacity.
Input: ([5], [10], 4)
Expected Output: (0, [])
Explanation: The only item weighs 5 but capacity is 4, so it cannot be taken; value 0, empty set.
Input: ([5], [10], 5)
Expected Output: (10, [0])
Explanation: The single item exactly fits the capacity, so it is taken for value 10.
Input: ([1, 1, 1], [10, 20, 30], 2)
Expected Output: (50, [1, 2])
Explanation: Capacity 2 holds two unit-weight items; the two highest-value ones are indices 1 (20) and 2 (30) for total 50.
Input: ([4, 5, 1], [1, 2, 3], 0)
Expected Output: (0, [])
Explanation: Capacity 0 means nothing can be taken; value 0, empty set.
Input: ([2, 2, 2], [3, 3, 3], 6)
Expected Output: (9, [0, 1, 2])
Explanation: All three items fit exactly within capacity 6 for total value 9.
Hints
- Use a 1D dp array of size W+1 and iterate the capacity from W down to weights[i] so each item is counted at most once. Iterating ascending instead would turn it into the unbounded variant.
- The rolling 1D dp forgets *which* items were taken. Record a keep[i][c] flag whenever taking item i strictly improves dp[c], then reconstruct by walking items backward from capacity W.
- Edge cases: empty item list, an item heavier than W (never fits), and W == 0 should all return value 0 and an empty chosen set.