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

    Anirudh KhuranaBy Anirudh Khurana11 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
    Anirudh Khurana
    • Website

    Anirudh Khurana is the founder of Code and Debug, an Indian EdTech platform that helps students and working professionals crack coding interviews and move into data and AI roles. He teaches DSA, Python, SQL, data engineering and machine learning through live bootcamps and his YouTube channel, Code and Debug, which has over 50,000 subscribers. Every guide on this blog is written or reviewed by him and tested with real code.

    Related Posts

    Data Structures & Algorithms

    DSA Interview Cheat Sheet: All Patterns You Need to Know

    4 April 2026
    Data Structures & Algorithms

    Symmetric Tree | Leetcode 101 | Recursive DFS Approach

    2 September 2025
    Data Structures & Algorithms

    Binary Tree Right Side View | Leetcode 199 | BFS and DFS Approaches

    2 September 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Categories
    • AI & Machine Learning (4)
    • Data Structures & Algorithms (241)
      • Beginner (82)
      • Expert (52)
      • Intermediate (106)
    • Pandas (1)
    • Python (1)
    • Resume (1)
    • SQL (1)
    Recent Posts

    Confusion Matrix in Machine Learning: An Interactive Guide

    21 October 2026

    100 SQL Interview Questions with Queries and Answers (Freshers to Experienced)

    17 October 2026

    Logistic Regression in Machine Learning: Explained Visually (with Python)

    17 October 2026

    Linear Regression in Machine Learning: Explained Visually (with Python)

    17 October 2026

    100 Python Interview Questions and Answers for Freshers and Experienced (Free PDF)

    17 October 2026
    Facebook Instagram YouTube LinkedIn WhatsApp
    © 2025 Code and Debug. All rights reserved.

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