Hello! Welcome back to our journey through Dynamic Programming.
In our last lesson, we mastered the fundamentals of DP by converting a memoized solution for the Fibonacci problem into a more efficient tabulated (bottom-up) one. We worked with a 1D DP array, where the state was defined by a single variable, n.
Today, we're taking a significant step forward into the world of 2D Dynamic Programming. Your learning outcome is to solve the 0/1 Knapsack problem using dynamic programming. This is a classic and essential problem often encountered in technical interviews. It requires us to manage a state defined by two variables, making it a perfect example to build upon your existing DP knowledge.
1. What is the 0/1 Knapsack Problem?
Imagine you're a thief with a knapsack. You have a collection of items, each with a specific weight and profit. Your knapsack has a maximum weight capacity. Your goal is to choose which items to put in the knapsack to maximize your total profit, without exceeding the weight limit.
The "0/1" part is crucial: for each item, you have only two choices. You either take the entire item (1) or leave it behind (0). You cannot take a fraction of an item.
Let's watch a short video to get a clear definition of the problem.
0/1 knapsack problem-Dynamic Programming | Data structures and algorithms
This video from Jenny's Lectures CS IT clearly defines the 0/1 Knapsack problem and contrasts it with the Fractional Knapsack problem, which helps explain why a simple greedy approach won't work here.
Watch the first 3 minutes and 30 seconds of the video (00:00 - 03:32). Focus on understanding the problem statement and the core constraint of the '0/1' property.
As the video mentions, a simple greedy strategy (like picking items with the highest profit-to-weight ratio first) does not guarantee an optimal solution for the 0/1 Knapsack problem. This is because a high-ratio item might take up weight in a way that prevents you from picking other, more valuable combinations. This is exactly the kind of optimization problem where Dynamic Programming shines.
2. The Path to a DP Solution: From Recursion to Memoization
As we learned previously, the first step towards a DP solution is often to formulate a plain recursive solution. For each item, we face a decision:
- Include the item: We can only do this if its weight is less than or equal to the remaining capacity of the knapsack. If we include it, we get its profit, and the knapsack's remaining capacity decreases.
- Exclude the item: We simply move on to the next item without any change in profit or capacity.
The optimal solution will be the maximum of the profits from these two choices.
This logic gives us a recursive structure. A function knapSack(capacity, weights, profits, n) would represent the maximum profit we can get from the first n items with a given capacity.
Java Program 0-1 Knapsack Problem
The article 'Java Program 0-1 Knapsack Problem' from GeeksForGeeks provides a clear Java implementation of this recursive logic. Notice how it directly translates the 'include vs. exclude' choice into code.
Read the section 'Java Program for 0-1 Knapsack Problem using Recursion'. Pay close attention to the knapSack method's if-else structure, which represents the decision for each item. You don't need to run the code, just understand its logic.
This recursive solution has a time complexity of because it re-computes the same subproblems repeatedly. Just like with Fibonacci, we can optimize this by storing the results of subproblems. Since our state is defined by two variables (the number of items considered, n, and the remaining capacity, W), we'll need a 2D array for memoization.
Java Program 0-1 Knapsack Problem
The same GeeksForGeeks article shows how to apply memoization (top-down DP) to our recursive solution. This pattern should feel very familiar from our last lesson.
Now read the next section, 'Memoization Approach for 0/1 Knapsack Problem'. Notice the use of a 2D array dp[n][W] to store results. The core logic remains the same, but we add checks to avoid re-computation.
By memoizing, we reduce the time complexity to , where N is the number of items and W is the knapsack capacity. The space complexity is also for the dp table, plus the recursion stack space.
3. The Tabulated Solution (Bottom-Up DP)
Now, let's build the solution iteratively, which is the main goal for today. We'll create a 2D table (or matrix) to store the optimal profits for all subproblems.
- Let
dp[i][w]be the maximum profit we can achieve using the firstiitems with a knapsack of capacityw. - Our table will have
N+1rows (for items 0 toN) andW+1columns (for capacities 0 toW).
The recurrence relation we will use to fill each cell dp[i][w] is:
- If the weight of the
i-th item (wt[i-1]) is greater than the current capacityw, we cannot include it. So, the profit is the same as the profit without this item:dp[i][w] = dp[i-1][w]. - Otherwise, we choose the maximum of:
- Excluding the item:
dp[i-1][w] - Including the item:
profits[i-1] + dp[i-1][w - wt[i-1]]
- Excluding the item:
The final answer will be in the bottom-right cell of the table, dp[N][W].
Watching this process is the best way to understand it.
4.5 0/1 Knapsack - Two Methods - Dynamic Programming
This classic video from Abdul Bari provides an excellent, step-by-step walkthrough of building and filling the DP table for the 0/1 Knapsack problem. He first builds the intuition and then introduces the formal formula.
Watch the video from the beginning up to 15:39. This covers the problem definition, setting up the DP table, and filling it row by row using the logic we just discussed. Pay close attention to how each cell's value is derived from previous cells.
Now that you've seen the process, let's look at the complete Java implementation for the tabulation method.
Java Program 0-1 Knapsack Problem
Let's return to the GeeksForGeeks article for the final, clean implementation of the bottom-up approach.
Read the section titled 'Bottom-up Approach for 0/1 Knapsack Problem'. Compare the nested loops and the if-else logic in the Java code to the recurrence relation we just defined. This is the standard iterative solution for this problem.
Test your understanding!
Given the following items and a knapsack with capacity W = 5:
- Item 1:
weight = 2,profit = 60 - Item 2:
weight = 3,profit = 100 - Item 3:
weight = 5,profit = 120
What is the value of dp[2][5] in the tabulation table? This means, what is the maximum profit using only the first two items (Item 1 and Item 2) with a capacity of 5?
Show answer
Let's calculate dp[2][5]. We are considering Item 2 (weight=3, profit=100) and have a capacity of 5.
-
The weight of Item 2 (3) is less than or equal to the capacity (5), so we have two choices:
- Exclude Item 2: The profit would be
dp[1][5]. With only Item 1 (weight=2), we can fit it in a capacity of 5, sodp[1][5] = 60. - Include Item 2: The profit would be
profit[1] + dp[1][5 - weight[1]]which is100 + dp[1][5 - 3]or100 + dp[1][2].dp[1][2]means the max profit with just Item 1 and capacity 2. We can fit Item 1, sodp[1][2] = 60.- Therefore, the profit for including Item 2 is
100 + 60 = 160.
- Exclude Item 2: The profit would be
-
Comparing the two choices:
max(60, 160) = 160. -
So,
dp[2][5] = 160. This corresponds to picking both Item 1 and Item 2, as their combined weight is 5 and combined profit is 160.
4. Backtracking to Find the Items
The DP table gives us the maximum profit, but it doesn't immediately tell us which items we selected. To find the combination of items, we can backtrack from the final cell dp[N][W].
The logic is simple:
- Start at
dp[i][w] = dp[N][W]. - Compare
dp[i][w]withdp[i-1][w]. - If they are the same, it means the
i-th item was not included. We move up todp[i-1][w]. - If they are different, it means the
i-th item was included. We add this item to our solution set, subtract its profit and weight, and move todp[i-1][w - wt[i-1]]. - Repeat until
iorwbecomes 0.
The Abdul Bari video you watched earlier also demonstrates this process.
4.5 0/1 Knapsack - Two Methods - Dynamic Programming
Let's revisit the Abdul Bari video for a clear demonstration of the backtracking process.
Watch the segment from 16:55 to 19:12. This part shows exactly how to trace back through the filled table to determine which items constitute the optimal solution.
5. Space Optimization (An Interview Bonus)
For your interview preparation, it's great to know that this solution can be space-optimized. Notice that to compute any cell in row i, we only need information from the previous row, i-1. We don't need all N previous rows.
This allows us to reduce the space complexity from to just by using a 1D array. This is a common and impressive optimization to discuss in an interview.
Java Program 0-1 Knapsack Problem
The GeeksForGeeks article concludes with an implementation of this space-optimized approach. It's a bit more subtle but very efficient.
Study the final section, 'Space optimized Approach for 0/1 Knapsack Problem using Dynamic Programming'. The key trick is to iterate the inner loop (for capacity w) from W down to 0. This prevents us from using the information of an item multiple times in the same pass.
Conclusion
Congratulations on tackling your first 2D DP problem! The 0/1 Knapsack is a foundational algorithm, and the patterns you learned today are applicable to a wide variety of other problems.
Key Takeaways:
- The 0/1 Knapsack problem seeks to maximize profit within a weight constraint for indivisible items.
- It's a classic 2D DP problem because its state is defined by two variables: the item index and the remaining knapsack capacity.
- The core of the DP solution is the recurrence relation that decides whether to include or exclude an item at each step.
- The tabulated (bottom-up) solution builds a 2D table iteratively, solving smaller subproblems first. Its time complexity is .
- We can backtrack through the completed DP table to find which items were chosen.
- The solution can be space-optimized from to .
Preview of the Next Lesson:
We will continue practicing 2D dynamic programming by solving another classic problem: finding the Longest Common Subsequence (LCS). This problem has a slightly different structure but reinforces the core concepts of building a 2D DP table to find an optimal solution.