💡 Memoization avoids recomputing the same subproblems by caching results, drastically improving efficiency while keeping the recursive structure.
Intuition
Use recursion but store results for each (i, w) state to avoid repeated work.
Algorithm
- Initialize a 2D cache for states (i, w).
- Recursively compute max value for each state, returning cached results if available.
- For each item, decide to include or exclude it if it fits.
- Return the cached maximum value for the full problem.
💡 Memoization prunes the recursion tree by remembering answers to subproblems.
Recurrence:Same as brute force: f(i, w) = max(f(i-1, w), f(i-1, w - weights[i-1]) + values[i-1]) if weights[i-1] ≤ w else f(i-1, w)
def knapsack_memo(i, w, weights, values, memo):
if i == 0 or w == 0:
return 0
if (i, w) in memo:
return memo[(i, w)]
if weights[i-1] > w:
memo[(i, w)] = knapsack_memo(i-1, w, weights, values, memo)
else:
memo[(i, w)] = max(knapsack_memo(i-1, w, weights, values, memo),
knapsack_memo(i-1, w - weights[i-1], weights, values, memo) + values[i-1])
return memo[(i, w)]
# Driver code
if __name__ == '__main__':
weights = [10, 20, 30]
values = [60, 100, 120]
W = 50
n = len(weights)
memo = {}
print(knapsack_memo(n, W, weights, values, memo))
Line Notes
if (i, w) in memo:Check if result already computed to avoid recomputation
memo[(i, w)] = knapsack_memo(i-1, w, weights, values, memo)Store result when skipping item
memo[(i, w)] = max(...)Store result when choosing best of include/exclude
return memo[(i, w)]Return cached or newly computed result
import java.util.HashMap;
import java.util.Map;
public class Knapsack {
static Map<String, Integer> memo = new HashMap<>();
public static int knapsackMemo(int i, int w, int[] weights, int[] values) {
if (i == 0 || w == 0) return 0;
String key = i + "," + w;
if (memo.containsKey(key)) return memo.get(key);
int result;
if (weights[i - 1] > w) {
result = knapsackMemo(i - 1, w, weights, values);
} else {
result = Math.max(knapsackMemo(i - 1, w, weights, values),
knapsackMemo(i - 1, w - weights[i - 1], weights, values) + values[i - 1]);
}
memo.put(key, result);
return result;
}
public static void main(String[] args) {
int[] weights = {10, 20, 30};
int[] values = {60, 100, 120};
int W = 50;
int n = weights.length;
System.out.println(knapsackMemo(n, W, weights, values));
}
}
Line Notes
if (memo.containsKey(key)) return memo.get(key);Return cached result if available
memo.put(key, result);Cache computed result for current state
if (weights[i - 1] > w)Skip item if it doesn't fit
Math.max(knapsackMemo(i - 1, w, weights, values),Choose max of exclude/include
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
unordered_map<string, int> memo;
int knapsackMemo(int i, int w, const vector<int>& weights, const vector<int>& values) {
if (i == 0 || w == 0) return 0;
string key = to_string(i) + "," + to_string(w);
if (memo.find(key) != memo.end()) return memo[key];
if (weights[i - 1] > w) {
memo[key] = knapsackMemo(i - 1, w, weights, values);
} else {
memo[key] = max(knapsackMemo(i - 1, w, weights, values),
knapsackMemo(i - 1, w - weights[i - 1], weights, values) + values[i - 1]);
}
return memo[key];
}
int main() {
vector<int> weights = {10, 20, 30};
vector<int> values = {60, 100, 120};
int W = 50;
int n = weights.size();
cout << knapsackMemo(n, W, weights, values) << endl;
return 0;
}
Line Notes
if (memo.find(key) != memo.end()) return memo[key];Return cached result if available
memo[key] = knapsackMemo(i - 1, w, weights, values);Cache result when skipping item
memo[key] = max(...)Cache result when choosing best option
return memo[key];Return cached or computed result
const memo = new Map();
function knapsackMemo(i, w, weights, values) {
if (i === 0 || w === 0) return 0;
const key = i + ',' + w;
if (memo.has(key)) return memo.get(key);
let result;
if (weights[i - 1] > w) {
result = knapsackMemo(i - 1, w, weights, values);
} else {
result = Math.max(knapsackMemo(i - 1, w, weights, values),
knapsackMemo(i - 1, w - weights[i - 1], weights, values) + values[i - 1]);
}
memo.set(key, result);
return result;
}
// Driver code
const weights = [10, 20, 30];
const values = [60, 100, 120];
const W = 50;
const n = weights.length;
console.log(knapsackMemo(n, W, weights, values));
Line Notes
if (memo.has(key)) return memo.get(key);Return cached result if available
memo.set(key, result);Cache computed result for reuse
if (weights[i - 1] > w)Skip item if it doesn't fit
Math.max(knapsackMemo(i - 1, w, weights, values),Choose max of exclude/include
TimeO(n * W)
SpaceO(n * W) due to memoization cache and recursion stack
Each state (i, w) is computed once and stored, reducing exponential calls to polynomial.
💡 For n=1000 and W=1000, this means about 1 million states, which is feasible.
Interview Verdict: Accepted
Memoization makes the solution efficient enough for typical constraints.