Cosmic Module

D

Qubits of DPK

January 16, 2026

Core DSA

UNBOUNDED KNAPSACK — ALL VERSIONS (JAVA)

Unbounded rule:
After

1️⃣ RECURSION (Brute Force)

java
QUBITS OF DPK
1public class Solution {
2
3    public int solve(int[] values, int[] weights, int capacity) {
4        int n = values.length;
5        // Start from last item
6        return unboundedRec(n - 1, values, weights, capacity);
7    }
8
9    // index -> items allowed: 0..index
10    // capacity -> remaining capacity
11    public int unboundedRec(int index, int[] values,
12                            int[] weights, int capacity) {
13
14        // Base case: no items OR no capacity
15        if (index < 0 || capacity == 0) {
16            return 0;
17        }
18
19        // Case 1: Skip current item
20        int skip = unboundedRec(index - 1, values, weights, capacity);
21
22        // Case 2: Pick current item (stay at SAME index)
23        int pick = 0;
24        if (weights[index] <= capacity) {
25            pick = values[index]
26                 + unboundedRec(index, values, weights,
27                                capacity - weights[index]);
28        }
29
30        return Math.max(pick, skip);
31    }
32}

⏱ Complexity

  • Time: Exponential
  • Space: O(N) (recursion stack)

2️⃣ MEMOIZATION (Top-Down DP)

java
QUBITS OF DPK
1import java.util.Arrays;
2
3public class Solution {
4
5    public int solve(int[] values, int[] weights, int capacity) {
6        int n = values.length;
7
8        // dp[index][capacity] = max value for this state
9        int[][] dp = new int[n][capacity + 1];
10
11        // Initialize with -1 (uncomputed)
12        for (int i = 0; i < n; i++) {
13            Arrays.fill(dp[i], -1);
14        }
15
16        return unboundedMemo(n - 1, values, weights, capacity, dp);
17    }
18
19    public int unboundedMemo(int index, int[] values,
20                             int[] weights, int capacity,
21                             int[][] dp) {
22
23        if (index < 0 || capacity == 0) {
24            return 0;
25        }
26
27        // If already solved, reuse
28        if (dp[index][capacity] != -1) {
29            return dp[index][capacity];
30        }
31
32        // Skip current item
33        int skip = unboundedMemo(index - 1, values, weights, capacity, dp);
34
35        // Pick current item (index stays SAME)
36        int pick = 0;
37        if (weights[index] <= capacity) {
38            pick = values[index]
39                 + unboundedMemo(index, values, weights,
40                                 capacity - weights[index], dp);
41        }
42
43        dp[index][capacity] = Math.max(pick, skip);
44        return dp[index][capacity];
45    }
46}

⏱ Complexity

  • Time: O(N × C)
  • Space: O(N × C) + recursion stack

3️⃣ TABULATION (Bottom-Up DP)

java
QUBITS OF DPK
1public class Solution {
2
3    public int solve(int[] values, int[] weights, int capacity) {
4        int n = values.length;
5
6        // dp[i][c] = max value using first i items with capacity c
7        int[][] dp = new int[n + 1][capacity + 1];
8
9        // Base cases:
10        // dp[0][*] = 0  (no items)
11        // dp[*][0] = 0  (no capacity)
12
13        for (int i = 1; i <= n; i++) {
14            for (int c = 1; c <= capacity; c++) {
15
16                // Skip current item
17                dp[i][c] = dp[i - 1][c];
18
19                // Pick current item (UNBOUNDED → same row)
20                if (weights[i - 1] <= c) {
21                    dp[i][c] = Math.max(
22                        dp[i][c],
23                        dp[i][c - weights[i - 1]] + values[i - 1]
24                    );
25                }
26            }
27        }
28
29        return dp[n][capacity];
30    }
31}

Key difference vs 0/1

plain text
QUBITS OF DPK
1dp[i][c - weight]   // same row → reuse allowed

⏱ Complexity

  • Time: O(N × C)
  • Space: O(N × C)

4️⃣ 1D SPACE OPTIMIZED (MOST IMPORTANT)

java
QUBITS OF DPK
1public class Solution {
2
3    public int solve(int[] values, int[] weights, int capacity) {
4        int n = values.length;
5
6        // dp[c] = max value with capacity c
7        int[] dp = new int[capacity + 1];
8
9        // Loop through items
10        for (int i = 0; i < n; i++) {
11
12            // IMPORTANT:
13            // Loop capacity FORWARD to allow reuse
14            for (int c = weights[i]; c <= capacity; c++) {
15
16                dp[c] = Math.max(
17                    dp[c],                        // skip
18                    dp[c - weights[i]] + values[i] // pick (reuse allowed)
19                );
20            }
21        }
22
23        return dp[capacity];
24    }
25}

Why forward loop?

It allows the

⏱ Complexity

  • Time: O(N × C)
  • Space: O(C) ⭐

ONE-LINE DIFFERENCE (MEMORIZE)

plain text
QUBITS OF DPK
1// 0/1 Knapsack → capacity loop BACKWARD
2for (c = C; c >= wt; c--)
3
4// Unbounded Knapsack → capacity loop FORWARD
5for (c = wt; c <= C; c++)

15-second interview summary

“Unbounded knapsack allows reusing items.
In recursion and memoization, we stay on the same index after picking.
In tabulation and 1D DP, we loop capacity forward to allow reuse.”