Given a set of positive integers and an integer k, check if there is any non-empty subset that sums to k.

For example,

Input:
 
A = { 7, 3, 2, 5, 8 }
k = 14
 
Output: Subset with the given sum exists
 
Subset { 7, 2, 5 } sums to 14

Practice this problem

A naive solution would be to cycle through all subsets of n numbers and, for every one of them, check if the subset sums to the right number. The running time is of order O(2n.n) since there are 2n subsets, and to check each subset, we need to sum at most n elements.

 
A better exponential-time algorithm uses recursion. Subset sum can also be thought of as a special case of the 0–1 Knapsack problem. For each item, there are two possibilities:

  1. Include the current item in the subset and recur for the remaining items with the remaining total.
  2. Exclude the current item from the subset and recur for the remaining items.

Finally, return true if we get a subset by including or excluding the current item; otherwise, return false. The recursion’s base case would be when no items are left, or the sum becomes negative. Return true when the sum becomes 0, i.e., the subset is found.

 
Following is the C++, Java, and Python implementation of the idea:

C++


Download  Run Code

Output:

Subsequence with the given sum exists

Java


Download  Run Code

Output:

Subsequence with the given sum exists

Python


Download  Run Code

Output:

Subsequence with the given sum exists

The time complexity of the above solution is exponential and occupies space in the call stack.

 
The problem has an optimal substructure. That means the problem can be broken down into smaller, simple “subproblems”, which can further be divided into yet simpler, smaller subproblems until the solution becomes trivial. The above solution also exhibits overlapping subproblems. If we draw the solution’s recursion tree, we can see that the same subproblems are getting computed repeatedly.

We know that problems with optimal substructure and overlapping subproblems can be solved using dynamic programming, where subproblem solutions are memoized rather than computed again and again. Following is the memoized implementation in C++, Java, and Python, which follows the top-down approach since we first break the problem into subproblems and then calculate and store values.

C++


Download  Run Code

Output:

Subsequence with the given sum exists

Java


Download  Run Code

Output:

Subsequence with the given sum exists

Python


Download  Run Code

Output:

Subsequence with the given sum exists

The time complexity of the above solution is O(n × sum) and requires O(n × sum) extra space, where n is the size of the input and sum is the sum of all elements in the input.

 
We can also solve this problem in a bottom-up manner. In the bottom-up approach, we solve smaller subproblems first, then solve larger subproblems from them. The following bottom-up approach computes T[i][j], for each 1 <= i <= n and 1 <= j <= sum, which is true if subset with sum j can be found using items up to first i items. It uses the value of smaller values i and j already computed. It has the same asymptotic runtime as Memoization but no recursion overhead.

Following is the C++, Java, and Python implementation of the idea:

C++


Download  Run Code

Output:

Subsequence with the given sum exists

Java


Download  Run Code

Output:

Subsequence with the given sum exists

Python


Download  Run Code

Output:

Subsequence with the given sum exists