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»Subset Sum Problem | Solved using Dynamic Programming
    Data Structures & Algorithms

    Subset Sum Problem | Solved using Dynamic Programming

    codeanddebugBy codeanddebug11 August 2025No Comments6 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Featured image of a question to solve subset sum problem
    Share
    Facebook Twitter LinkedIn Pinterest Email

    Given an array of positive integers arr[] and a value sum, determine if there is a subset of arr[] with sum equal to given sum. 

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

    Examples:

    Input: arr[] = [3, 34, 4, 12, 5, 2], sum = 9
    Output: true 
    Explanation: Here there exists a subset with target sum = 9, 4+3+2 = 9.
    Input: arr[] = [3, 34, 4, 12, 5, 2], sum = 30
    Output: false
    Explanation: There is no subset with target sum 30.
    Input: arr[] = [1, 2, 3], sum = 6
    Output: true
    Explanation: The entire array can be taken as a subset, giving 1 + 2 + 3 = 6.

    Constraints:
    1 <= arr.size() <= 200
    1<= arr[i] <= 200
    1<= sum <= 104

    Contents:
    • 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: Tabulation (Space Optimization)
      • Intuition and Approach
      • Code Implementation
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Final Notes

    Solution 1: Recursion (Brute Force)

    Intuition and Approach

    At each index, you decide to include the current element (if it doesn’t exceed target) or exclude it. This forms a binary decision tree. Base cases:

    • If target == 0 → True (empty subset achieves 0).
    • If index == 0 → True only if arr equals target.
      This explores all subsets but with exponential time due to overlapping subproblems.

    Code Implementation

    class Solution:
        def solve(self, index, target, arr):
            # Base: target achieved
            if target == 0:
                return True
            # Base: only first element available
            if index == 0:
                if arr == target:
                    return True
                return False
            # Choice: pick only if feasible
            if arr[index] > target:
                pick = False
            else:
                pick = self.solve(index - 1, target - arr[index], arr)
            # Choice: not pick current element
            not_pick = self.solve(index - 1, target, arr)
            # Return if any choice leads to True
            return pick or not_pick
    
        def isSubsetSum(self, arr, sum):
            n = len(arr)
            # Start from the last index aiming for target sum
            return self.solve(n - 1, sum, arr)

    Code Explanation

    • The function solve(index, target) tries both decisions: include arr[index] (if allowed) or exclude it.
    • If target hits 0 at any depth, it signals a valid subset.
    • The first-element base case avoids negative indices and settles the smallest subproblem.

    Time and Space Complexity

    • Precise: Time O(2^n), as each element can be picked or not; Space O(n) recursion depth.
    • Simplified: Exponential time, linear stack space.

    Conclusion

    Good for intuition and very small inputs, but inefficient due to repeated subproblems.


    Solution 2: Memoization (Top-Down DP)

    Intuition and Approach

    The same (index, target) states recur many times. Cache results in a 2D table dp[index][target]:

    • Before computing, return cached dp if already set.
    • Otherwise, compute pick/not-pick as recursion and store True/False.

    Code Implementation

    class Solution:
        def solve(self, index, target, arr, dp):
            # If target achieved
            if target == 0:
                return True
            # If only first element can be used
            if index == 0:
                if arr[0] == target:
                    return True
                return False
            # Use memoized result if available
            if dp[index][target] != -1:
                return dp[index][target]
            # Try picking current element if feasible
            if arr[index] > target:
                pick = False
            else:
                pick = self.solve(index - 1, target - arr[index], arr, dp)
            # Or skip current element
            not_pick = self.solve(index - 1, target, arr, dp)
            # Store boolean result in dp
            return pick or not_pick
    
        def isSubsetSum(self, arr, sum):
            n = len(arr)
            # dp[index][target] = -1 (unknown), else True/False
            dp = [[-1 for _ in range(sum + 1)] for _ in range(n)]
            return self.solve(n - 1, sum, arr, dp)

    Code Explanation

    • dp[index][target] caches whether a subset of arr[0..index] can form target.
    • Converts exponential recursion into polynomial by avoiding recomputation of identical states.

    Time and Space Complexity

    • Precise: Time O(n * sum), Space O(n * sum) for dp + O(n) stack.
    • Simplified: Pseudo-polynomial in target, linear stack.

    Conclusion

    Efficient and interview-ready; ideal when sum is not too large.


    Solution 3: Tabulation (Bottom-Up DP)

    Intuition and Approach

    Build a boolean table dp[n][sum+1] where dp[i][t] indicates if you can make sum t using arr[0..i].

    • Initialize dp[i] = True for all i (target 0 is always achievable).
    • Initialize dp[arr] = True if it doesn’t exceed sum.
    • Transition:
      • If arr[i] > t → pick = False else pick = dp[i-1][t – arr[i]]
      • not_pick = dp[i-1][t]
      • dp[i][t] = pick or not_pick

    Code Implementation

    class Solution:
        def isSubsetSum(self, arr, sum):
            n = len(arr)
            # dp[i][t] -> can we make sum t using first i elements (0..i)?
            dp = [[False for _ in range(sum + 1)] for _ in range(n)]
            # Sum 0 achievable by picking empty subset
            for index in range(n):
                dp[index] = True
            # Handle first element
            if arr <= sum:
                dp[arr] = True
            # Fill table for remaining elements
            for index in range(1, n):
                for target in range(0, sum + 1):
                    if arr[index] > target:
                        pick = False
                    else:
                        pick = dp[index - 1][target - arr[index]]
                    not_pick = dp[index - 1][target]
                    dp[index][target] = pick or not_pick
            # Final answer: can we form 'sum' using all elements?
            return dp[n - 1][sum]

    Code Explanation

    • Iteratively fills achievable sums per prefix of the array.
    • Each state depends only on the previous row, enabling further space optimization.

    Time and Space Complexity

    • Precise: Time O(n * sum), Space O(n * sum).
    • Simplified: Pseudo-polynomial time/space.

    Conclusion

    Deterministic and easy to debug; preferred when memory is acceptable.


    Solution 4: Tabulation (Space Optimization)

    Intuition and Approach

    Current dp row depends only on the previous row, so we can roll arrays:

    • Maintain prev (for i-1) and curr (for i).
    • After computing curr for all targets, assign prev = curr.

    Code Implementation

    class Solution:
        def isSubsetSum(self, arr, sum):
            n = len(arr)
            # prev[t] -> achievable with elements up to previous index
            prev = [False for _ in range(sum + 1)]
            prev = True  # sum 0 is always achievable
            if arr <= sum:
                prev[arr] = True
            # Iterate elements
            for index in range(1, n):
                curr = [False for _ in range(sum + 1)]
                for target in range(0, sum + 1):
                    if arr[index] > target:
                        pick = False
                    else:
                        pick = prev[target - arr[index]]
                    not_pick = prev[target]
                    curr[target] = pick or not_pick
                # Move window
                prev = curr
            return prev[sum]

    Code Explanation

    • Uses two 1D arrays to track reachable sums, preserving correctness with O(sum) space.
    • Same transitions as full table but memory efficient.

    Time and Space Complexity

    • Precise: Time O(n * sum), Space O(sum).
    • Simplified: Pseudo-polynomial time, linear space in target.

    Conclusion

    Best practical version for large n with moderate target sum; commonly expected in interviews.


    Final Notes

    • All approaches rely on the fundamental pick/not-pick decision per element.
    • Base cases ensure correctness: reaching target 0 is always True; the single-item check at index 0 anchors recursion/DP.
    • Memoization and tabulation both achieve O(n * sum) time; the space-optimized tabulation reduces memory to O(sum).
    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 Medium
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleCherry Pickup II | Leetcode 1463 | Solved using Dynamic Programming
    Next Article Partition Equal Subset Sum | Leetcode 416
    codeanddebug
    • Website

    Related Posts

    Data Structures & Algorithms

    Minimum sum partition | Solved using Tabulation in Python

    11 August 2025
    Data Structures & Algorithms

    Partition Equal Subset Sum | Leetcode 416

    11 August 2025
    Data Structures & Algorithms

    Cherry Pickup II | Leetcode 1463 | Solved using Dynamic Programming

    11 August 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Categories
    • Data Structures & Algorithms (181)
      • Beginner (68)
      • Expert (42)
      • Intermediate (71)
    • Uncategorised (1)
    Recent Posts

    Minimum sum partition | Solved using Tabulation in Python

    11 August 2025

    Partition Equal Subset Sum | Leetcode 416

    11 August 2025

    Subset Sum Problem | Solved using Dynamic Programming

    11 August 2025

    Cherry Pickup II | Leetcode 1463 | Solved using Dynamic Programming

    11 August 2025

    Trapping Rain Water | Leetcode 42 | Brute, Better and Optimal Solution using Stack

    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.