Given an array arr of non-negative integers and an integer target, the task is to count all subsets of the array whose sum is equal to the given target.
Examples:
Input: arr[] = [5, 2, 3, 10, 6, 8], target = 10
Output: 3
Explanation: The subsets {5, 2, 3}, {2, 8}, and {10} sum up to the target 10.Input: arr[] = [2, 5, 1, 4, 3], target = 10
Output: 3
Explanation: The subsets {2, 1, 4, 3}, {5, 1, 4}, and {2, 5, 3} sum up to the target 10.Input: arr[] = [5, 7, 8], target = 3
Output: 0
Explanation: There are no subsets of the array that sum up to the target 3.Input: arr[] = [35, 2, 8, 22], target = 0
Output: 1
Explanation: The empty subset is the only subset with a sum of 0.
Table of Content
Using Recursion - O(2 ^ n) Time and O(n) Space
A recursive approach is to consider wo cases for each element in the array:
- Exclude the current element: The element at index i is not included in the subset, and the current sum remains unchanged. This leads to the recursion call countSubsets(i + 1, currentSum, target).
- Include the current element: The element at index i is included in the subset, and the sum is updated as currentSum + arr[i]. This leads to the recursion call countSubsets(i + 1, currentSum + arr[i], target).
#include <iostream>
#include <vector>
using namespace std;
// Recursive function to count subsets with the given sum
int countSubsets(int i, int currentSum, int target, vector<int> &arr)
{
int n = arr.size();
// If all elements have been processed,
// check whether the current subset sum equals the target
if (i == n)
{
return (currentSum == target);
}
// Exclude the current element
int exclude = countSubsets(i + 1, currentSum, target, arr);
// Include the current element if the sum
// does not exceed the target
int include = 0;
if (currentSum + arr[i] <= target)
{
include = countSubsets(i + 1, currentSum + arr[i], target, arr);
}
// Return the total count of valid subsets
return include + exclude;
}
// Function to count subsets with sum equal to target
int perfectSum(vector<int> &arr, int target)
{
// Start recursion from index 0
// with an initial sum of 0
return countSubsets(0, 0, target, arr);
}
int main()
{
vector<int> arr = {35, 2, 8, 22};
int target = 0;
cout << perfectSum(arr, target);
return 0;
}
public class GFG {
// Recursive function to count subsets with the given
// sum
public static int countSubsets(int i, int currentSum,
int target, int[] arr)
{
int n = arr.length;
// If all elements have been processed
if (i == n) {
return (currentSum == target) ? 1 : 0;
}
// Exclude the current element
int exclude
= countSubsets(i + 1, currentSum, target, arr);
// Include the current element if the sum does not
// exceed target
int include = 0;
if (currentSum + arr[i] <= target) {
include = countSubsets(
i + 1, currentSum + arr[i], target, arr);
}
// Return total count
return include + exclude;
}
// Function to count subsets with sum equal to target
public static int perfectSum(int[] arr, int target)
{
return countSubsets(0, 0, target, arr);
}
public static void main(String[] args)
{
int[] arr = { 35, 2, 8, 22 };
int target = 0;
System.out.println(perfectSum(arr, target));
}
}
def countSubsets(i, currentSum, target, arr):
n = len(arr)
# If all elements have been processed,
# check whether the current subset sum equals the target
if i == n:
return 1 if currentSum == target else 0
# Exclude the current element
exclude = countSubsets(i + 1, currentSum, target, arr)
# Include the current element if the sum
# does not exceed the target
include = 0
if currentSum + arr[i] <= target:
include = countSubsets(i + 1, currentSum + arr[i], target, arr)
# Return the total count of valid subsets
return include + exclude
# Function to count subsets with sum equal to target
def perfectSum(arr, target):
# Start recursion from index 0
# with an initial sum of 0
return countSubsets(0, 0, target, arr)
if __name__ == "__main__":
arr = [35, 2, 8, 22]
target = 0
print(perfectSum(arr, target))
using System;
class GFG {
// Recursive function to count subsets with the given
// sum
static int CountSubsets(int i, int currentSum,
int target, int[] arr)
{
int n = arr.Length;
// If all elements have been processed
if (i == n) {
return (currentSum == target) ? 1 : 0;
}
// Exclude the current element
int exclude
= CountSubsets(i + 1, currentSum, target, arr);
// Include the current element if the sum does not
// exceed the target
int include = 0;
if (currentSum + arr[i] <= target) {
include = CountSubsets(
i + 1, currentSum + arr[i], target, arr);
}
// Return the total count
return include + exclude;
}
// Function to count subsets with sum equal to target
static int perfectSum(int[] arr, int target)
{
// Start recursion from index 0 with an initial sum
// of 0
return CountSubsets(0, 0, target, arr);
}
static void Main()
{
int[] arr = { 35, 2, 8, 22 };
int target = 0;
Console.WriteLine(perfectSum(arr, target));
}
}
function countSubsets(i, currentSum, target, arr)
{
const n = arr.length;
// If all elements have been processed,
// check whether the current subset sum equals the
// target
if (i === n) {
return currentSum === target ? 1 : 0;
}
// Exclude the current element
const exclude
= countSubsets(i + 1, currentSum, target, arr);
// Include the current element if the sum
// does not exceed the target
let include = 0;
if (currentSum + arr[i] <= target) {
include = countSubsets(i + 1, currentSum + arr[i],
target, arr);
}
// Return the total count of valid subsets
return include + exclude;
}
// Function to count subsets with sum equal to target
function perfectSum(arr, target)
{
// Start recursion from index 0
// with an initial sum of 0
return countSubsets(0, 0, target, arr);
}
// Driver Code
const arr = [ 35, 2, 8, 22 ];
const target = 0;
console.log(perfectSum(arr, target));
Output
1
Using Top-Down Dp (memoization) - O(n * target) Time and O(n * target) Space
If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming:
While applying a recursive approach in this problem, we notice that certain subproblems are computed multiple times. For example while considering arr = [1, 1, 2, 3] and target = 10, countSubsets(3, 4, 10, arr) computed multiple times from countSubsets(2, 0, 10, arr) and countSubsets(2, 2, 10, arr).
Working of Approach:
- Start from index 0 with a current sum of 0 and recursively decide whether to include or exclude each element.
- Use a memoization table memo[i][currentSum] to store the number of valid subsets for each state (i, currentSum) and avoid recomputation.
- If a state has already been computed, return its stored value directly.
- For every element, recursively count subsets by excluding it and, if possible, including it without exceeding the target sum.
- When all elements are processed, return 1 if the current sum equals the target; otherwise return 0. The sum of all valid recursive paths is the answer.
#include <bits/stdc++.h>
using namespace std;
// Function to recursively count subsets with a given sum
// using memoization to avoid redundant calculations
int countSubsets(int i, int currentSum, int target,
vector<int> &arr, vector<vector<int>> &memo) {
// Get the size of the array
int n = arr.size();
// Base case: If we've processed all elements in the array
if (i == n)
// Return 1 if the current subset's
// sum equals the target, else return 0
return (currentSum == target);
// Check if the result for the current state is already computed
if (memo[i][currentSum] != -1)
return memo[i][currentSum];
// Case 1: Exclude the current element and
// move to the next
int exclude = countSubsets(i + 1, currentSum, target, arr, memo);
// Case 2: Include the current element in the subset
int include = 0;
// Only include the current element if
// adding it does not exceed the target sum
if ((arr[i] + currentSum) <= target)
include = countSubsets(i + 1, currentSum + arr[i], target, arr, memo);
// Store the result in the memoization table
// and return it
return memo[i][currentSum] = (include + exclude);
}
// Function to initiate the recursive subset count with memoization
// Parameters:
// - arr: Input array of integers
// - target: Target sum for the subsets
int perfectSum(vector<int> &arr, int target) {
// Get the size of the array
int n = arr.size();
// Initialize a 2D memoization table with -1
// Rows represent indices in the array
// Columns represent possible sums up to the target
vector<vector<int>> memo(n + 1, vector<int>(target + 1, -1));
// Start the recursion from the first element with
// a current sum of 0
return countSubsets(0, 0, target, arr, memo);
}
int main() {
vector<int> arr = {35, 2, 8, 22};
int target = 0;
cout << perfectSum(arr, target);
return 0;
}
import java.util.Arrays;
class GFG {
// Function to recursively count
// subsets with a given sum using memoization
static int countSubsets(int i, int currentSum, int target,
int[] arr, int[][] memo) {
int n = arr.length;
// Base case: If we've processed all elements in the array
if (i == n)
// Return 1 if the current subset's sum
// equals the target, else return 0
return (currentSum == target) ? 1 : 0;
// Check if the result for the current state
// is already computed
if (memo[i][currentSum] != -1)
return memo[i][currentSum];
// Case 1: Exclude the current element
int exclude = countSubsets(i + 1, currentSum, target, arr, memo);
// Case 2: Include the current element
int include = 0;
if (currentSum + arr[i] <= target)
include = countSubsets(i + 1, currentSum + arr[i],
target, arr, memo);
// Store the result in the memoization
// table and return it
memo[i][currentSum] = include + exclude;
return memo[i][currentSum];
}
// Function to initiate the recursive subset count
static int perfectSum(int[] arr, int target) {
int n = arr.length;
// Initialize a 2D memoization table with -1
int[][] memo = new int[n + 1][target + 1];
for (int[] row : memo)
Arrays.fill(row, -1);
return countSubsets(0, 0, target, arr, memo);
}
public static void main(String[] args) {
int[] arr = {35, 2, 8, 22};
int target = 0;
System.out.println(perfectSum(arr, target));
}
}
def countSubsets(i, currentSum, target, arr, memo):
n = len(arr)
# Base case: If we've processed all elements
if i == n:
return 1 if currentSum == target else 0
# Check if result is already computed
if memo[i][currentSum] != -1:
return memo[i][currentSum]
# Case 1: Exclude the current element
exclude = countSubsets(i + 1, currentSum, target, arr, memo)
# Case 2: Include the current element
include = 0
if currentSum + arr[i] <= target:
include = countSubsets(i + 1, currentSum + arr[i], target, arr, memo)
# Store result in memoization table and return it
memo[i][currentSum] = include + exclude
return memo[i][currentSum]
def perfectSum(arr, target):
n = len(arr)
# Initialize a 2D memoization table with -1
memo = [[-1 for _ in range(target + 1)] for _ in range(n + 1)]
return countSubsets(0, 0, target, arr, memo)
if __name__ == "__main__":
arr = [35, 2, 8, 22]
target = 0
print(perfectSum(arr, target))
using System;
class GFG {
// Function to recursively count subsets with a
// given sum using memoization
static int CountSubsets(int i, int currentSum,
int target, int[] arr,
int[, ] memo) {
int n = arr.Length;
// Base case: If we've processed all elements
if (i == n)
return (currentSum == target) ? 1 : 0;
// Check if result is already computed
if (memo[i, currentSum] != -1)
return memo[i, currentSum];
// Case 1: Exclude the current element
int exclude = CountSubsets(i + 1, currentSum,
target, arr, memo);
// Case 2: Include the current element
int include = 0;
if (currentSum + arr[i] <= target)
include
= CountSubsets(i + 1, currentSum + arr[i],
target, arr, memo);
// Store result in memoization table and return it
memo[i, currentSum] = include + exclude;
return memo[i, currentSum];
}
// Function to initiate the recursive subset count
static int perfectSum(int[] arr, int target) {
int n = arr.Length;
// Initialize a 2D memoization table with -1
int[, ] memo = new int[n + 1, target + 1];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= target; j++)
memo[i, j] = -1;
return CountSubsets(0, 0, target, arr, memo);
}
static void Main(string[] args) {
int[] arr = { 35, 2, 8, 22 };
int target = 0;
Console.WriteLine(perfectSum(arr, target));
}
}
function countSubsets(i, currentSum, target, arr, memo) {
const n = arr.length;
// Base case: If we've processed all elements
if (i === n) {
return currentSum === target ? 1 : 0;
}
// Check if result is already computed
if (memo[i][currentSum] !== -1) {
return memo[i][currentSum];
}
// Case 1: Exclude the current element
const exclude = countSubsets(i + 1, currentSum, target,
arr, memo);
// Case 2: Include the current element
let include = 0;
if (currentSum + arr[i] <= target) {
include = countSubsets(i + 1, currentSum + arr[i],
target, arr, memo);
}
// Store result in memoization table and return it
memo[i][currentSum] = include + exclude;
return memo[i][currentSum];
}
function perfectSum(arr, target) {
const n = arr.length;
// Initialize a 2D memoization table with -1
const memo = Array.from(
{length : n + 1}, () => Array(target + 1).fill(-1));
// Start recursion
return countSubsets(0, 0, target, arr, memo);
}
//Driver Code
const arr = [ 35, 2, 8, 22 ];
const target = 0;
console.log(perfectSum(arr, target));
Output
1
Using Dynamic Programming (Tabulation) - O(n * target) Time and O(n * target) Space
The idea is to build a 2D DP table, where dp[i][j] stores the number of subsets that can be formed using the first i elements with sum equal to j. For each element, we consider two choices: exclude it or include it (if it does not exceed the current sum). The final answer is stored in dp[n][target].
Working of Approach:
- Create a 2D DP table dp[n + 1][target + 1], where dp[i][j] stores the number of subsets from the first i elements having sum j.
- Initialize all entries as 0 and set dp[0][0] = 1, since the empty subset is the only subset with sum 0.
- Process each array element one by one. For every sum j, first copy dp[i - 1][j] (excluding the current element).
- If j >= arr[i - 1], include the current element by adding dp[i - 1][j - arr[i - 1]] to dp[i][j].
- After filling the table, dp[n][target] gives the total number of subsets whose sum is equal to the target.
Let us understand with an example:
Input: arr[] = [35, 2, 8, 22], target = 0
- Initialize dp[0][0] = 1 since the empty subset is the only subset with sum 0.
- Process each element (35, 2, 8, 22) one by one and copy the previous value of dp[i-1][0] to dp[i][0].
- Since j = 0 and every array element is greater than 0, the condition j >= arr[i-1] is never satisfied. Hence, no element is included, and dp[i][0] remains 1.
- Therefore, dp[i][0] remains 1 after processing every element.
- Finally, dp[4][0] = 1, indicating that the empty subset {} is the only subset with sum 0.
#include <bits/stdc++.h>
using namespace std;
int perfectSum(vector<int> &arr, int target) {
// Get the size of the input array
int n = arr.size();
// Create a 2D DP table with dimensions (n+1) x (target+1)
// dp[i][j] represents the number of ways to achieve a sum 'j'
// using the first 'i' elements of the array
vector<vector<int>> dp(n + 1, vector<int>(target + 1, 0));
// Base case: There's exactly one way to achieve a
// sum of 0 (by selecting no elements)
dp[0][0] = 1;
// Fill the DP table
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= target; j++) {
// First, consider excluding the current element
dp[i][j] = dp[i - 1][j];
// Then, consider including the current element
// (if the remaining sum allows it)
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][j - arr[i - 1]];
}
}
}
// Return the number of ways to achieve the
// target sum using all elements in the array
return dp[n][target];
}
int main() {
vector<int> arr = {35, 2, 8, 22};
int target = 0;
cout << perfectSum(arr, target);
return 0;
}
import java.util.Arrays;
class GFG {
// Function to count the number of subsets
// with a sum equal to the target using tabular DP
static int perfectSum(int[] arr, int target) {
int n = arr.length;
// Create a 2D DP table
int[][] dp = new int[n + 1][target + 1];
// Base case: There's one way to achieve a
// sum of 0 (by selecting no elements)
dp[0][0] = 1;
// Fill the DP table
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= target; j++) {
// Exclude the current element
dp[i][j] = dp[i - 1][j];
// Include the current element if
// it doesn't exceed the current sum
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][j - arr[i - 1]];
}
}
}
// Return the number of ways to achieve the target
// sum
return dp[n][target];
}
public static void main(String[] args) {
int[] arr = { 35, 2, 8, 22 };
int target = 0;
System.out.println(perfectSum(arr, target));
}
}
def perfectSum(arr, target):
n = len(arr)
# Create a 2D DP table
dp = [[0] * (target + 1) for _ in range(n + 1)]
# Base case: There's one way to achieve
# a sum of 0 (by selecting no elements)
dp[0][0] = 1
# Fill the DP table
for i in range(1, n + 1):
for j in range(target + 1):
# Exclude the current element
dp[i][j] = dp[i - 1][j]
# Include the current element
# if it doesn't exceed the current sum
if j >= arr[i - 1]:
dp[i][j] += dp[i - 1][j - arr[i - 1]]
# Return the number of ways to achieve
# the target sum
return dp[n][target]
if __name__ == "__main__":
arr = [35, 2, 8, 22]
target = 0
print(perfectSum(arr, target))
using System;
class GFG {
// Function to count the number of subsets
// with a sum equal to the target using tabular DP
static int perfectSum(int[] arr, int target) {
int n = arr.Length;
// Create a 2D DP table
int[, ] dp = new int[n + 1, target + 1];
// Base case: There's one way to
// achieve a sum of 0 (by selecting no elements)
dp[0, 0] = 1;
// Fill the DP table
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= target; j++) {
// Exclude the current element
dp[i, j] = dp[i - 1, j];
// Include the current element
// if it doesn't exceed the current sum
if (j >= arr[i - 1]) {
dp[i, j] += dp[i - 1, j - arr[i - 1]];
}
}
}
// Return the number of ways to achieve the target
// sum
return dp[n, target];
}
static void Main(string[] args) {
int[] arr = { 35, 2, 8, 22 };
int target = 0;
Console.WriteLine(perfectSum(arr, target));
}
}
function perfectSum(arr, target)
{
const n = arr.length;
// Create a 2D DP table
const dp = Array.from({length : n + 1},
() => Array(target + 1).fill(0));
// Base case: There's one way to achieve
// a sum of 0 (by selecting no elements)
dp[0][0] = 1;
// Fill the DP table
for (let i = 1; i <= n; i++) {
for (let j = 0; j <= target; j++) {
// Exclude the current element
dp[i][j] = dp[i - 1][j];
// Include the current element if it doesn't
// exceed the current sum
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][j - arr[i - 1]];
}
}
}
// Return the number of ways to achieve
// the target sum
return dp[n][target];
}
// Driver Code
const arr = [ 35, 2, 8, 22 ];
const target = 0;
console.log(perfectSum(arr, target));
Output
1
Using Space Optimised DP - O(n * target) Time and O(target) Space
The idea is to use two 1D DP arrays. prev[j] stores the number of subsets with sum j using the processed elements. For each array element, update a new DP array by considering both excluding and including the current element. After processing all elements, prev[target] gives the required count.
Working of Approach:
- Initialize prev[0] = 1 since the empty subset is the only subset with sum 0.
- Process each array element one by one and copy prev into curr.
- For every possible sum j, include the current element if j >= arr[i-1] by adding prev[j - arr[i-1]] to curr[j].
- After processing the current element, assign curr to prev for the next iteration.
- After all elements are processed, prev[target] stores the total number of subsets with the required sum.
#include <bits/stdc++.h>
using namespace std;
// Function to calculate the number of subsets with a given sum
// Parameters:
// - arr: Input array of integers
// - target: Target sum for the subsets
int perfectSum(vector<int> &arr, int target) {
int n = arr.size();
// Create two 1D DP arrays: `prev` for the previous state
// and `curr` for the current state
vector<int> prev(target + 1, 0), curr(target + 1, 0);
// Base case: There's one way to achieve a sum
// of 0 (by selecting no elements)
prev[0] = 1;
// Iterate through the elements of the array
for (int i = 1; i <= n; i++) {
// Start by copying the previous state
// to the current state
curr = prev;
// Update the current DP array for sums up to the target
for (int j = 0; j <= target; j++) {
// If the current element can be included in the subset
if (j >= arr[i - 1]) {
curr[j] += prev[j - arr[i - 1]];
}
}
// Move to the next state by updating
// `prev` to `curr`
prev = curr;
}
// Return the number of ways to
// achieve the target sum
return curr[target];
}
int main() {
vector<int> arr = {35, 2, 8, 22};
int target = 0;
cout << perfectSum(arr, target);
return 0;
}
import java.util.*;
class GFG {
// Function to calculate the number of subsets with a
// given sum
static int perfectSum(int[] arr, int target) {
int n = arr.length;
// Create two 1D DP arrays: `prev` for the previous
// state and `curr` for the current state
int[] prev = new int[target + 1];
int[] curr = new int[target + 1];
// Base case: There's one way to achieve a sum
// of 0 (by selecting no elements)
prev[0] = 1;
// Iterate through the elements of the array
for (int i = 1; i <= n; i++) {
// Start by copying the previous state
// to the current state
System.arraycopy(prev, 0, curr, 0, target + 1);
// Update the current DP array for sums up to
// the target
for (int j = 0; j <= target; j++) {
// If the current element can be included in
// the subset
if (j >= arr[i - 1]) {
curr[j] += prev[j - arr[i - 1]];
}
}
// Move to the next state by updating `prev` to
// `curr`
System.arraycopy(curr, 0, prev, 0, target + 1);
}
// Return the number of ways to achieve the target
// sum
return curr[target];
}
public static void main(String[] args) {
int[] arr = { 35, 2, 8, 22 };
int target = 0;
System.out.println(perfectSum(arr, target));
}
}
def perfectSum(arr, target):
n = len(arr)
# Create two 1D DP arrays: `prev` for the previous state
# and `curr` for the current state
prev = [0] * (target + 1)
curr = [0] * (target + 1)
# Base case: There's one way to achieve a sum
# of 0 (by selecting no elements)
prev[0] = 1
# Iterate through the elements of the array
for i in range(1, n + 1):
# Start by copying the previous state
# to the current state
curr = prev[:]
# Update the current DP array for sums up
# to the target
for j in range(target + 1):
# If the current element can be included
# in the subset
if j >= arr[i - 1]:
curr[j] += prev[j - arr[i - 1]]
# Move to the next state by updating `prev` to `curr`
prev = curr[:]
# Return the number of ways to achieve the target sum
return curr[target]
arr = [35, 2, 8, 22]
target = 0
print(perfectSum(arr, target))
using System;
class GFG {
// Function to calculate the number of subsets with a
// given sum
static int perfectSum(int[] arr, int target) {
int n = arr.Length;
// Create two 1D DP arrays: `prev` for the previous
// state and `curr` for the current state
int[] prev = new int[target + 1];
int[] curr = new int[target + 1];
// Base case: There's one way to achieve a sum
// of 0 (by selecting no elements)
prev[0] = 1;
// Iterate through the elements of the array
for (int i = 1; i <= n; i++) {
// Start by copying the previous state
// to the current state
Array.Copy(prev, curr, target + 1);
// Update the current DP array for sums up to
// the target
for (int j = 0; j <= target; j++) {
// If the current element can be included in
// the subset
if (j >= arr[i - 1]) {
curr[j] += prev[j - arr[i - 1]];
}
}
// Move to the next state by updating `prev` to
// `curr`
Array.Copy(curr, prev, target + 1);
}
// Return the number of ways to achieve the target
// sum
return curr[target];
}
static void Main(string[] args) {
int[] arr = { 35, 2, 8, 22 };
int target = 0;
Console.WriteLine(perfectSum(arr, target));
}
}
function perfectSum(arr, target)
{
let n = arr.length;
// Create two 1D DP arrays: `prev` for the previous
// state and `curr` for the current state
let prev = new Array(target + 1).fill(0);
let curr = new Array(target + 1).fill(0);
// Base case: There's one way to achieve a sum
// of 0 (by selecting no elements)
prev[0] = 1;
// Iterate through the elements of the array
for (let i = 1; i <= n; i++) {
// Start by copying the previous state
// to the current state
curr = [...prev ];
// Update the current DP array for sums up to the
// target
for (let j = 0; j <= target; j++) {
// If the current element can be included in the
// subset
if (j >= arr[i - 1]) {
curr[j] += prev[j - arr[i - 1]];
}
}
// Move to the next state by updating `prev` to
// `curr`
prev = [...curr ];
}
// Return the number of ways to achieve
// the target sum
return curr[target];
}
// Driver Code
const arr = [ 35, 2, 8, 22 ];
const target = 0;
console.log(perfectSum(arr, target));
Output
1