Unbounded Knapsack
Problem Statement
Given n items with weights wt[] and values val[], and a knapsack capacity W, determine the maximum total value you can obtain. You can use **each item multiple times**.
Example: Input: W = 8, wt = [2,3,5], val = [50,100,140], Output: 300 (take two 3-weight items and one 2-weight)
Approach 1: Recursion
Explanation: For each item, decide how many times to include it (0 to W/wt[i]) recursively.
Time Complexity: Exponential
Space Complexity: O(n) recursion stack
function UnboundedKnapsack(W, n): if n == 0 or W == 0: return 0 if wt[n-1] <= W: return max( val[n-1] + UnboundedKnapsack(W - wt[n-1], n), UnboundedKnapsack(W, n-1) ) else: return UnboundedKnapsack(W, n-1)
Approach 2: Memoization (Top-Down DP)
Explanation: Store results for subproblems (W, n) to avoid recomputation.
Time Complexity: O(n * W)
Space Complexity: O(n * W) DP array + O(n) recursion stack
dp[0..n][0..W] = -1 function UnboundedKnapsack(W, n): if n == 0 or W == 0: return 0 if dp[n][W] != -1: return dp[n][W] if wt[n-1] <= W: dp[n][W] = max( val[n-1] + UnboundedKnapsack(W - wt[n-1], n), UnboundedKnapsack(W, n-1) ) else: dp[n][W] = UnboundedKnapsack(W, n-1) return dp[n][W]
Approach 3: Tabulation (Bottom-Up DP)
Explanation: Iteratively fill DP table where dp[i][w] represents max value using first i items for weight w.
Time Complexity: O(n * W)
Space Complexity: O(n * W)
dp[0..n][0..W] = 0 for i = 1 to n: for w = 0 to W: if wt[i-1] <= w: dp[i][w] = max(val[i-1] + dp[i][w - wt[i-1]], dp[i-1][w]) else: dp[i][w] = dp[i-1][w] return dp[n][W]
Approach 4: Space Optimization
Explanation: Use a 1D DP array, update dp[w] for each item to save space.
Time Complexity: O(n * W)
Space Complexity: O(W)
dp[0..W] = 0 for i = 0 to n-1: for w = wt[i] to W: dp[w] = max(dp[w], val[i] + dp[w - wt[i]]) return dp[W]
