Close Menu
    Code and Debug
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook X (Twitter) Instagram YouTube WhatsApp
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook Instagram YouTube LinkedIn WhatsApp
    Code and Debug – BlogCode and Debug – Blog
    Code and Debug – BlogCode and Debug – Blog
    Home»Data Structures & Algorithms»0 – 1 Knapsack Problem | 5 DP Solutions in Python
    Data Structures & Algorithms

    0 – 1 Knapsack Problem | 5 DP Solutions in Python

    codeanddebugBy codeanddebug13 August 2025No Comments7 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Share
    Facebook Twitter LinkedIn Pinterest Email

    Given n items, each with a specific weight and value, and a knapsack with a capacity of W, the task is to put the items in the knapsack such that the sum of weights of the items <= W and the sum of values associated with them is maximized. 

    Here’s the [Problem Link] to begin with.

    Note: You can either place an item entirely in the bag or leave it out entirely. Also, each item is available in single quantity.

    Examples :

    Input: W = 4, val[] = [1, 2, 3], wt[] = [4, 5, 1] 
    Output: 3
    Explanation: Choose the last item, which weighs 1 unit and has a value of 3.
    Input: W = 3, val[] = [1, 2, 3], wt[] = [4, 5, 6] 
    Output: 0
    Explanation: Every item has a weight exceeding the knapsack's capacity (3).
    Input: W = 5, val[] = [10, 40, 30, 50], wt[] = [5, 4, 2, 3] 
    Output: 80
    Explanation: Choose the third item (value 30, weight 2) and the last item (value 50, weight 3) for a total value of 80.

    Constraints:
    2 ≤ val.size() = wt.size() ≤ 103
    1 ≤ W ≤ 103
    1 ≤ val[i] ≤ 103
    1 ≤ wt[i] ≤ 103

    Contents:
     [show]
    • Solution 1: Recursion (Brute Force)
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 2: Memoization (Top-Down DP)
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 3: Tabulation (Bottom-Up DP)
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 4: Space Optimization using 2 One-Dimensional Arrays
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 5: Space Optimization using a Single 1D Array
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
    • Final Notes and Tips

    Solution 1: Recursion (Brute Force)

    Intuition and Approach

    At each item index:

    • If the item fits (wt[index] ≤ W), consider:
      • pick = val[index] + best for remaining capacity (W − wt[index]) with previous items
      • not_pick = best without taking this item
    • Take max(pick, not_pick).
    • Base case: at index 0, if it fits, return its value; else 0.

    This explores all subsets and forms the basis for memoization.

    Code Implementation

    class Solution:
        def solve(self, index, W, val, wt):
            # Base: only first item available
            if index == 0:
                if wt[index] <= W:
                    return val[index]
                return 0
            # If current item doesn't fit, can't pick it
            if wt[index] > W:
                pick = float("-inf")
            else:
                # Option to pick current item
                pick = val[index] + self.solve(index - 1, W - wt[index], val, wt)
            # Option to not pick current item
            not_pick = 0 + self.solve(index - 1, W, val, wt)
            # Max of both choices
            return max(pick, not_pick)
    
        def knapsack(self, W, val, wt):
            n = len(wt)
            return self.solve(n - 1, W, val, wt)

    Code Explanation

    • Recursively tries both decisions where feasible and returns the maximum achievable value.
    • The base case anchors the recursion at the first item.

    Time and Space Complexity

    • Precise: Time O(2^n), Space O(n) recursion depth.
    • Simplified: Exponential time; linear stack space.

    Conclusion

    Great for understanding the choice structure, but too slow for larger n or W.


    Solution 2: Memoization (Top-Down DP)

    Intuition and Approach

    The same (index, W) states repeat frequently. Cache results in a 2D dp table to avoid recomputation:

    • dp[index][W] stores the best value achievable considering items up to index with capacity W.
    • Before computing, return cached value if available.

    Code Implementation

    class Solution:
        def solve(self, index, W, val, wt, dp):
            # Base: first item
            if index == 0:
                if wt[index] <= W:
                    return val[index]
                return 0
            # Cached result
            if dp[index][W] != -1:
                return dp[index][W]
            # Decision: pick if it fits, else -inf
            if wt[index] > W:
                pick = float("-inf")
            else:
                pick = val[index] + self.solve(index - 1, W - wt[index], val, wt, dp)
            not_pick = 0 + self.solve(index - 1, W, val, wt, dp)
            dp[index][W] = max(pick, not_pick)
            return dp[index][W]
    
        def knapsack(self, W, val, wt):
            n = len(wt)
            dp = [[-1 for _ in range(0, W + 1)] for _ in range(n)]
            return self.solve(n - 1, W, val, wt, dp)

    Code Explanation

    • Stores results for each state to ensure each is solved once.
    • Converts exponential recursion into polynomial time.

    Time and Space Complexity

    • Precise: Time O(n × W), Space O(n × W) for dp + O(n) stack.
    • Simplified: Polynomial time and space; suitable for typical constraints.

    Conclusion

    Ideal upgrade over pure recursion, retaining clarity while being efficient.


    Solution 3: Tabulation (Bottom-Up DP)

    Intuition and Approach

    Iteratively build dp where dp[i][w] is the max value using items up to index i with capacity w.

    • Initialize first row: for all capacities w ≥ wt, dp[w] = val.
    • Transition:
      • If wt[i] ≤ w: pick = val[i] + dp[i−1][w − wt[i]]
      • not_pick = dp[i−1][w]
      • dp[i][w] = max(pick, not_pick)

    Code Implementation

    class Solution:
        def knapsack(self, W, val, wt):
            n = len(wt)
            # dp[i][w] -> max value using items up to i with capacity w
            dp = [[0 for _ in range(0, W + 1)] for _ in range(n)]
            # Initialize first item row
            for w in range(0, W + 1):
                if wt <= w:
                    dp[w] = val
            # Fill table
            for index in range(1, n):
                for W_ in range(0, W + 1):
                    if wt[index] > W_:
                        pick = float("-inf")
                    else:
                        pick = val[index] + dp[index - 1][W_ - wt[index]]
                    not_pick = 0 + dp[index - 1][W_]
                    dp[index][W_] = max(pick, not_pick)
            return dp[n - 1][W]

    Code Explanation

    • Bottom-up computation ensures all subproblems are solved once in increasing order of i and capacity.
    • Final answer resides at dp[n−1][W].

    Time and Space Complexity

    • Precise: Time O(n × W), Space O(n × W).
    • Simplified: Polynomial time and space; easy to trace and debug.

    Conclusion

    A robust, iterative DP that’s straightforward to implement and reason about.


    Solution 4: Space Optimization using 2 One-Dimensional Arrays

    Intuition and Approach

    Each row depends only on the previous row, so keep just two 1D arrays:

    • prev for dp[i−1][·] and curr for dp[i][·].
    • After finishing a row, assign prev = curr.

    Code Implementation

    class Solution:
        def knapsack(self, W, val, wt):
            n = len(wt)
            prev = [0 for _ in range(0, W + 1)]
            # Initialize first item row
            for w in range(0, W + 1):
                if wt <= w:
                    prev[w] = val
            # Build next rows
            for index in range(1, n):
                curr = [0 for _ in range(0, W + 1)]
                for W_ in range(0, W + 1):
                    if wt[index] > W_:
                        pick = float("-inf")
                    else:
                        pick = val[index] + prev[W_ - wt[index]]
                    not_pick = 0 + prev[W_]
                    curr[W_] = max(pick, not_pick)
                prev = curr
            return prev[W]

    Code Explanation

    • Reduces space from O(n × W) to O(W) while preserving all transitions.
    • Maintains correctness by using the previous row only.

    Time and Space Complexity

    • Precise: Time O(n × W), Space O(W).
    • Simplified: Same time, linear space in capacity.

    Conclusion

    A practical and memory-efficient improvement that’s often required in interviews.


    Solution 5: Space Optimization using a Single 1D Array

    Intuition and Approach

    Update the same array in reverse capacity order to avoid overwriting needed values:

    • Iterate W_ from W down to 0 for each item to ensure dp[w − wt[i]] still refers to the previous iteration’s state.

    Code Implementation

    class Solution:
        def knapsack(self, W, val, wt):
            n = len(wt)
            prev = [0 for _ in range(0, W + 1)]
            # Initialize first item
            for w in range(0, W + 1):
                if wt <= w:
                    prev[w] = val
            # Single-array optimization: iterate capacity backward
            for index in range(1, n):
                for W_ in range(W, -1, -1):
                    if wt[index] > W_:
                        pick = float("-inf")
                    else:
                        pick = val[index] + prev[W_ - wt[index]]
                    not_pick = 0 + prev[W_]
                    prev[W_] = max(pick, not_pick)
            return prev[W]

    Code Explanation

    • Backward iteration ensures correctness by preventing intra-row contamination.
    • Achieves the optimal O(W) space with clean logic.

    Time and Space Complexity

    • Precise: Time O(n × W), Space O(W).
    • Simplified: Best practical DP for classic 0-1 Knapsack.

    Final Notes and Tips

    • Base initialization for the first item row is crucial to seed the DP correctly.
    • Using float(“-inf”) for “cannot pick” keeps max logic clean; alternatively, conditionally skip computing pick.
    • The single-array backward iteration is the optimal space pattern for 0-1 knapsack; forward iteration would incorrectly reuse updated states.
    • For very large W, consider value-based DP or alternative heuristics, but for typical constraints, these DP formulations are standard.

    Mastering these five formulations, especially the transition from recursion to O(W) space, builds strong dynamic programming intuition and prepares for a wide range of knapsack-like problems.

    Join our Advance DSA COURSE

    For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.

    Dynamic Programming on Subsequence Hard
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticlePartitions with Given Difference | Optimal Solution
    codeanddebug
    • Website

    Related Posts

    Data Structures & Algorithms

    Partitions with Given Difference | Optimal Solution

    13 August 2025
    Data Structures & Algorithms

    Perfect Sum Problem | All 4 DP Solutions in Python

    13 August 2025
    Data Structures & Algorithms

    Minimum sum partition | Solved using Tabulation in Python

    11 August 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Categories
    • Data Structures & Algorithms (184)
      • Beginner (68)
      • Expert (43)
      • Intermediate (73)
    • Uncategorised (1)
    Recent Posts

    0 – 1 Knapsack Problem | 5 DP Solutions in Python

    13 August 2025

    Partitions with Given Difference | Optimal Solution

    13 August 2025

    Perfect Sum Problem | All 4 DP Solutions in Python

    13 August 2025

    Minimum sum partition | Solved using Tabulation in Python

    11 August 2025

    Partition Equal Subset Sum | Leetcode 416

    11 August 2025
    Facebook Instagram YouTube LinkedIn WhatsApp
    © 2025 Code and Debug. All rights reserved.

    Type above and press Enter to search. Press Esc to cancel.