Open In App

Count of Subsets that can be partitioned into two non empty sets with equal Sum

Last Updated : 16 Feb, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array Arr[] of size N, the task is to find the count of subsets of Arr[] that can be partitioned into two non-empty groups having equal sum.

Examples: 

Input: Arr[] = {2, 3, 4, 5}
Output: 2
Explanation: The subsets are: 
{2, 3, 5} which can be split into {2, 3} and {5}
{2, 3, 4, 5} which can be split into {2, 5} and {3, 4}

Input: Arr[] = {1, 2, 3, 4, 5}
Output: 7

 

Naive Approach: The simplest approach is to generate all subsets and for each subset S, if it has a sum of A, find all the subsets of S and check if it has a sum of A/2. 

Time Complexity: O(4N
Auxiliary Space: O(1)

Efficient Approach: The idea to solve the problem efficiently is to use the meet in the middle technique. 

  • Split the array equally into two halves and for each half compute all the possible subsets.
  • For elements included in a subset in either half, assign it to either first group or second group by adding to or subtracting from the variable ‘sum‘ ( sum is initialized to 0).
  • For the first half of the array, if an element is assigned to the first group simply add its value to sum, else subtract it. For the second half of the array, if an element is assigned to the first group subtract it from sum, else add it.

Follow the below steps to solve the problem:

  • Declare a global Boolean array ‘canSplit’ of size(1 << N) to store the result of each subset if it can be partitioned into two groups having equal sum.
  • Declare a global Map to assign the sum of a subset to an ID, and to find if there is already a subset with the same sum as current subset.
  • Declare a global Vector of Vector to store all the bitmasks having the same sum together.
  • Define a Recursive function say makeSubsets1(i, sum, mask) to construct all subsets from the first half of the array.
    • Base Case, If i == N/2, then the first half of the array has been traversed completely.
      • If the sum of the current subset represented by bitmask is different from the already formed subsets, then increment the variable ID by 1 and assign the value of sum to this ID.
      • Retrieve the ID number of the current sum.
      • Add the subset represented by the bitmask to the vector of the retrieved ID.
    • The number at the current index can be:
      • Excluded from the current subset.
      • Included to the first group of the subset. In this case, we add the number to ‘sum‘.
      • Included to the second group of the subset. In this case, we subtract the number from ‘sum‘.
  • Define a Recursive function say makeSubsets2(i, sum, mask) to construct all subsets from the second half of the array.
    • Base Case, If i == N, then the entire array has been traversed completely starting from the middle.
      • If any subset in the first half of the array has the same sum as that of the current subset, then the Bitwise OR of the two forms a valid subset as they have the same unbalance value. In other words if :
        • ∑(First group)1 – ∑(Second group) = ∑(Second group)2 – ∑(First group)2, by rearraning terms,
        • ∑(First group)1 + ∑(First group)2 = ∑(Second group)1 + ∑(Second group)2
      • Hence the Bitwise OR of the two subsets can be divided into two groups having equal sums. Iterate through all the subsets of first half having the same sum and mark the Bitwise OR of it with the current subset from the second half as ‘true‘ indicating a valid subset.
    • The number at the current index can be:
      • Excluded from the current subset.
      • Included to the second group of the subset. In this case, we add the number to ‘sum‘.
      • Included to the first group of the subset. In this case, we subtract the number from ‘sum‘.
  • Iterate through all the subsets and increase the answer by 1 if canSplit[mask] = true .
  • Print the answer.

Below is the code for the above approach:

C++14




// C++ program for the above approach.
 
#include <bits/stdc++.h>
using namespace std;
 
// Boolean array to check if elements
// represented by a bitmask(subset)
// can be split into two non-empty groups
// having equal sum.
bool canSplit[1 << 20];
 
// Map to store sum of subsets
// and find if there is a subset
// with the current sum.
map<int, int> mp;
 
// Vector of vector to store
// all the bitmasks having the
// same sum together.
vector<vector<int> > subsets(1 << 20);
 
// Variable to count subsets
// having unique sums.
int ID;
 
// Function to generate all possible
// subsets from first half
// of the array.
void makeSubsets1(int i, int N, int sum,
                  int mask, int Arr[])
{
 
    // If first half of the array
    // is traversed.
    if (i == N) {
 
        // If none of the previously formed
        // subsets have the same sum
        // as the current subset.
        if (mp.find(sum) == mp.end()) {
 
            // Increase ID by 1 as the
            // subsets having a unique
            // sum value has increased by 1.
            ++ID;
 
            // Assign the value of sum to ID.
            mp[sum] = ID;
        }
 
        // Retrieve the subset number
        // having this sum.
        int id = mp[sum];
 
        // Add the bitmask to the vector
        // of this particular subset id.
        subsets[id].push_back(mask);
        return;
    }
 
    // Exclude the current element
    // from the subset
    makeSubsets1(i + 1, N, sum,
                 mask, Arr);
 
    // Include the current element
    // to the first group of the subset.
    makeSubsets1(i + 1, N, sum + Arr[i],
                 mask | (1 << i), Arr);
 
    // Include the current element
    // to the second group of the subset.
    makeSubsets1(i + 1, N, sum - Arr[i], mask | (1 << i),
                 Arr);
}
 
// Function to generate all possible
// subsets from second half of array.
void makeSubsets2(int i, int N, int sum,
                  int mask, int Arr[])
{
 
    // If the second half
    // of the array is traversed.
    if (i == N) {
 
        // If the current subset sum has
        // occurred before in the
        // first part of the array, then the
        // combined subset from both halves
        // of the array forms a valid subset
        if (mp.find(sum) != mp.end()) {
 
            // Iterate through all the bitmasks
            // from the first part of the array
            // having the same current sum.
            for (auto num : subsets[mp[sum]]) {
 
                // Mark the bitwise OR
                // of both subsets as TRUE.
                canSplit[num | mask] = 1;
            }
        }
        return;
    }
 
    // Exclude the current element
    // from the subset.
    makeSubsets2(i + 1, N, sum, mask, Arr);
 
    // Include the current element
    // to the second group of the subset.
    makeSubsets2(i + 1, N, sum + Arr[i], mask | (1 << i),
                 Arr);
 
    // Include the current element
    // to the first group of the subset.
    makeSubsets2(i + 1, N, sum - Arr[i], mask | (1 << i),
                 Arr);
}
 
// Utility function to find all subsets from both halves of
// the array.
int UtilCountOfSubsets(int N, int Arr[])
{
    // Split the array into two parts.
    int mid = N / 2;
 
    // Function calls
    makeSubsets1(0, mid, 0, 0, Arr);
    makeSubsets2(mid, N, 0, 0, Arr);
 
    int ans = 0;
 
    // Iterate through all bitmasks
    // from 1 to 2^N - 1.
    for (int i = 1; i < (1 << N); ++i) {
 
        // If canSplit[i] is true,
        // increase the answer by 1.
        ans += canSplit[i];
    }
 
    // Return the answer.
    return ans;
}
 
// Driver code
int main()
{
 
    // Input Array
    int Arr[] = { 2, 3, 4, 5 };
 
    // Size of array
    int N = sizeof(Arr) / sizeof(Arr[0]);
 
    cout << UtilCountOfSubsets(N, Arr) << endl;
}


Java




import java.util.*;
import java.io.*;
 
// Java program for the above approach
class GFG{
 
  // Boolean array to check if elements
  // represented by a bitmask(subset)
  // can be split into two non-empty groups
  // having equal sum.
  public static Boolean canSplit[] = new Boolean[1 << 20];
 
  // Map to store sum of subsets
  // and find if there is a subset
  // with the current sum.
  public static TreeMap<Integer, Integer> mp = new TreeMap<Integer, Integer>();
 
  // Vector of vector to store
  // all the bitmasks having the
  // same sum together.
  public static ArrayList<ArrayList<Integer>> subsets = new ArrayList<ArrayList<Integer>>();
 
  // Variable to count subsets
  // having unique sums.
  public static int ID;
 
  // Function to generate all possible
  // subsets from first half
  // of the array.
  public static void makeSubsets1(int i, int N, int sum, int mask, int Arr[])
  {
 
    // If first half of the array
    // is traversed.
    if (i == N) {
 
      // If none of the previously formed
      // subsets have the same sum
      // as the current subset.
      if (!mp.containsKey(sum)) {
 
        // Increase ID by 1 as the
        // subsets having a unique
        // sum value has increased by 1.
        ++ID;
 
        // Assign the value of sum to ID.
        mp.put(sum, ID);
      }
 
      // Retrieve the subset number
      // having this sum.
      int id = mp.get(sum);
 
      // Add the bitmask to the vector
      // of this particular subset id.
      subsets.get(id).add(mask);
      return;
    }
 
    // Exclude the current element
    // from the subset
    makeSubsets1(i + 1, N, sum, mask, Arr);
 
    // Include the current element
    // to the first group of the subset.
    makeSubsets1(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
 
    // Include the current element
    // to the second group of the subset.
    makeSubsets1(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
  }
 
  // Function to generate all possible
  // subsets from second half of array.
  public static void makeSubsets2(int i, int N, int sum, int mask, int Arr[])
  {
 
    // If the second half
    // of the array is traversed.
    if (i == N) {
 
      // If the current subset sum has
      // occurred before in the
      // first part of the array, then the
      // combined subset from both halves
      // of the array forms a valid subset
      if (mp.containsKey(sum)) {
 
        // Iterate through all the bitmasks
        // from the first part of the array
        // having the same current sum.
        for(Integer num : subsets.get(mp.get(sum))) {
 
          // Mark the bitwise OR
          // of both subsets as TRUE.
          canSplit[num | mask] = true;
        }
      }
      return;
    }
 
    // Exclude the current element
    // from the subset.
    makeSubsets2(i + 1, N, sum, mask, Arr);
 
    // Include the current element
    // to the second group of the subset.
    makeSubsets2(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
 
    // Include the current element
    // to the first group of the subset.
    makeSubsets2(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
  }
 
  // Utility function to find all subsets from both halves of
  // the array.
  public static int UtilCountOfSubsets(int N, int Arr[])
  {
    // Split the array into two parts.
    int mid = N / 2;
 
    // Function calls
    makeSubsets1(0, mid, 0, 0, Arr);
    makeSubsets2(mid, N, 0, 0, Arr);
 
    int ans = 0;
 
    // Iterate through all bitmasks
    // from 1 to 2^N - 1.
    for (int i = 1 ; i < (1 << N) ; ++i) {
 
      // If canSplit[i] is true,
      // increase the answer by 1.
      if(canSplit[i]){
        ans++;
      }
    }
 
    // Return the answer.
    return ans;
  }
 
  // Driver code
  public static void main(String args[])
  {
    int Arr[] = {2, 3, 4, 5};
    int N = Arr.length;
    for(int i = 0 ; i < (1<<20) ; i++){
      subsets.add(new ArrayList<Integer>());
      canSplit[i] = false;
    }
 
    System.out.println(UtilCountOfSubsets(N, Arr));
  }
}
 
// This code is contributed by subhamgoyal2014.


Python3




# python3 program for the above approach.
 
# Boolean array to check if elements
# represented by a bitmask(subset)
# can be split into two non-empty groups
# having equal sum.
canSplit = [0 for _ in range(1 << 20)]
 
# Map to store sum of subsets
# and find if there is a subset
# with the current sum.
mp = {}
 
# Vector of vector to store
# all the bitmasks having the
# same sum together.
subsets = [[] for _ in range(1 << 20)]
 
# Variable to count subsets
# having unique sums.
ID = 0
 
# Function to generate all possible
# subsets from first half
# of the array.
 
 
def makeSubsets1(i, N, sum, mask, Arr):
    global canSplit, mp, subsets, ID
 
    # If first half of the array
    # is traversed.
    if (i == N):
 
                # If none of the previously formed
                # subsets have the same sum
                # as the current subset.
        if (not sum in mp):
 
                        # Increase ID by 1 as the
                        # subsets having a unique
                        # sum value has increased by 1.
            ID += 1
 
            # Assign the value of sum to ID.
            mp[sum] = ID
 
            # Retrieve the subset number
            # having this sum.
        id = mp[sum]
 
        # Add the bitmask to the vector
        # of this particular subset id.
        subsets[id].append(mask)
        return
 
        # Exclude the current element
        # from the subset
    makeSubsets1(i + 1, N, sum, mask, Arr)
 
    # Include the current element
    # to the first group of the subset.
    makeSubsets1(i + 1, N, sum + Arr[i], mask | (1 << i), Arr)
 
    # Include the current element
    # to the second group of the subset.
    makeSubsets1(i + 1, N, sum - Arr[i], mask | (1 << i), Arr)
 
 
# Function to generate all possible
# subsets from second half of array.
def makeSubsets2(i, N, sum, mask, Arr):
    global canSplit, mp, subsets, ID
 
    # If the second half
    # of the array is traversed.
    if (i == N):
 
                # If the current subset sum has
                # occurred before in the
                # first part of the array, then the
                # combined subset from both halves
                # of the array forms a valid subset
        if (sum in mp):
 
                        # Iterate through all the bitmasks
                        # from the first part of the array
                        # having the same current sum.
            for num in subsets[mp[sum]]:
 
                                # Mark the bitwise OR
                                # of both subsets as TRUE.
                canSplit[num | mask] = 1
        return
 
        # Exclude the current element
        # from the subset.
    makeSubsets2(i + 1, N, sum, mask, Arr)
 
    # Include the current element
    # to the second group of the subset.
    makeSubsets2(i + 1, N, sum + Arr[i], mask | (1 << i),
                 Arr)
 
    # Include the current element
    # to the first group of the subset.
    makeSubsets2(i + 1, N, sum - Arr[i], mask | (1 << i),
                 Arr)
 
 
# Utility function to find all subsets from both halves of
# the array.
def UtilCountOfSubsets(N, Arr):
    global canSplit, mp, subsets, ID
    # Split the array into two parts.
    mid = N // 2
 
    # Function calls
    makeSubsets1(0, mid, 0, 0, Arr)
    makeSubsets2(mid, N, 0, 0, Arr)
 
    ans = 0
 
    # Iterate through all bitmasks
    # from 1 to 2^N - 1.
    for i in range(1, 1 << N):
 
                # If canSplit[i] is true,
                # increase the answer by 1.
        ans += canSplit[i]
 
        # Return the answer.
    return ans
 
 
# Driver code
if __name__ == "__main__":
 
        # Input Array
    Arr = [2, 3, 4, 5]
 
    # Size of array
    N = len(Arr)
 
    print(UtilCountOfSubsets(N, Arr))
 
    # This code is contributed by rakeshsahni


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
namespace GFG
{
    class Program
    {
        // Boolean array to check if elements
        // represented by a bitmask(subset)
        // can be split into two non-empty groups
        // having equal sum.
        public static Boolean[] canSplit = new Boolean[1 << 20];
 
        // Map to store sum of subsets
        // and find if there is a subset
        // with the current sum.
        public static SortedDictionary<int, int> mp = new SortedDictionary<int, int>();
 
        // Vector of vector to store
        // all the bitmasks having the
        // same sum together.
        public static List<List<int>> subsets = new List<List<int>>();
 
        // Variable to count subsets
        // having unique sums.
        public static int ID;
 
        // Function to generate all possible
        // subsets from first half
        // of the array.
        public static void makeSubsets1(int i, int N, int sum, int mask, int[] Arr)
        {
 
            // If first half of the array
            // is traversed.
            if (i == N)
            {
 
                // If none of the previously formed
                // subsets have the same sum
                // as the current subset.
                if (!mp.ContainsKey(sum))
                {
 
                    // Increase ID by 1 as the
                    // subsets having a unique
                    // sum value has increased by 1.
                    ++ID;
 
                    // Assign the value of sum to ID.
                    mp.Add(sum, ID);
                }
 
                // Retrieve the subset number
                // having this sum.
                int id = mp[sum];
 
                // Add the bitmask to the vector
                // of this particular subset id.
                subsets[id].Add(mask);
                return;
            }
 
            // Exclude the current element
            // from the subset
            makeSubsets1(i + 1, N, sum, mask, Arr);
 
            // Include the current element
            // to the first group of the subset.
            makeSubsets1(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
 
            // Include the current element
            // to the second group of the subset.
            makeSubsets1(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
        }
 
        // Function to generate all possible
        // subsets from second half of array.
        public static void makeSubsets2(int i, int N, int sum, int mask, int[] Arr)
        {
 
            // If the second half
            // of the array is traversed.
            if (i == N)
            {
 
                // If the current subset sum has
                // occurred before in the
                // first part of the array, then the
                // combined subset from both halves
                // of the array forms a valid subset
                if (mp.ContainsKey(sum))
                {
 
                    // Iterate through all the bitmasks
                    // from the first part of the array
                    // having the same current sum.
                    foreach (var num in subsets[mp[sum]])
                    {
 
                        // Mark the bitwise OR
                        // of both subsets as TRUE.
                        canSplit[num | mask] = true;
                    }
                }
                return;
            }
 
            // Exclude the current element
            // from the subset.
            makeSubsets2(i + 1, N, sum, mask, Arr);
 
            // Include the current element
            // to the second group of the subset.
            makeSubsets2(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
 
            // Include the current element
            // to the first group of the subset.
            makeSubsets2(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
        }
 
        // Utility function to find all subsets from both halves of
        // the array.
        public static int UtilCountOfSubsets(int N, int[] Arr)
        {
            // Split the array into two parts.
            int mid = N / 2;
 
            // Function calls
            makeSubsets1(0, mid, 0, 0, Arr);
            makeSubsets2(mid, N, 0, 0, Arr);
 
            int ans = 0;
 
            // Iterate through all bitmasks
            // from 1 to 2^N - 1.
            for (int i = 1; i < (1 << N); ++i)
            {
 
                // If canSplit[i] is true,
                // increase the answer by 1.
                if (canSplit[i])
                {
                    ans++;
                }
            }
 
            // Return the answer.
            return ans;
        }
 
        // Driver code
        public static void Main(string[] args)
        {
            int[] Arr = { 2, 3, 4, 5 };
            int N = Arr.Length;
            for (int i = 0; i < (1 << 20); i++)
            {
                subsets.Add(new List<int>());
                canSplit[i] = false;
            }
 
            Console.WriteLine(UtilCountOfSubsets(N, Arr));
        }
    }
}


Javascript




<script>
 
// javascript program for the above approach
class GFG
{
    // Boolean array to check if elements
    // represented by a bitmask(subset)
    // can be split into two non-empty groups
    // having equal sum.
     static canSplit = Array(1 << 20).fill(null);
    // Map to store sum of subsets
    // and find if there is a subset
    // with the current sum.
     static mp = new Map();
    // Vector of vector to store
    // all the bitmasks having the
    // same sum together.
     static subsets = new Array();
    // Variable to count subsets
    // having unique sums.
     static ID = 0;
    // Function to generate all possible
    // subsets from first half
    // of the array.
    static makeSubsets1(i, N, sum, mask, Arr)
    {
        // If first half of the array
        // is traversed.
        if (i == N)
        {
            // If none of the previously formed
            // subsets have the same sum
            // as the current subset.
            if (!GFG.mp.has(sum))
            {
                // Increase ID by 1 as the
                // subsets having a unique
                // sum value has increased by 1.
                ++GFG.ID;
                // Assign the value of sum to ID.
                GFG.mp.set(sum,GFG.ID);
            }
            // Retrieve the subset number
            // having this sum.
            var id = GFG.mp.get(sum);
            // Add the bitmask to the vector
            // of this particular subset id.
            (GFG.subsets[id].push(mask) > 0);
            return;
        }
        // Exclude the current element
        // from the subset
        GFG.makeSubsets1(i + 1, N, sum, mask, Arr);
        // Include the current element
        // to the first group of the subset.
        GFG.makeSubsets1(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
        // Include the current element
        // to the second group of the subset.
        GFG.makeSubsets1(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
    }
    // Function to generate all possible
    // subsets from second half of array.
    static makeSubsets2(i, N, sum, mask, Arr)
    {
        // If the second half
        // of the array is traversed.
        if (i == N)
        {
            // If the current subset sum has
            // occurred before in the
            // first part of the array, then the
            // combined subset from both halves
            // of the array forms a valid subset
            if (GFG.mp.has(sum))
            {
                // Iterate through all the bitmasks
                // from the first part of the array
                // having the same current sum.
                for ( const  num of GFG.subsets[GFG.mp.get(sum)]) {
                // Mark the bitwise OR
                // of both subsets as TRUE.
                GFG.canSplit[num | mask] = true;
}
            }
            return;
        }
        // Exclude the current element
        // from the subset.
        GFG.makeSubsets2(i + 1, N, sum, mask, Arr);
        // Include the current element
        // to the second group of the subset.
        GFG.makeSubsets2(i + 1, N, sum + Arr[i], mask | (1 << i), Arr);
        // Include the current element
        // to the first group of the subset.
        GFG.makeSubsets2(i + 1, N, sum - Arr[i], mask | (1 << i), Arr);
    }
    // Utility function to find all subsets from both halves of
    // the array.
    static UtilCountOfSubsets(N, Arr)
    {
        // Split the array into two parts.
        var mid = parseInt(N / 2);
        // Function calls
        GFG.makeSubsets1(0, mid, 0, 0, Arr);
        GFG.makeSubsets2(mid, N, 0, 0, Arr);
        var ans = 0;
        // Iterate through all bitmasks
        // from 1 to 2^N - 1.
        for (let i=1; i < (1 << N); ++i)
        {
            // If canSplit[i] is true,
            // increase the answer by 1.
            if (GFG.canSplit[i])
            {
                ans++;
            }
        }
        // Return the answer.
        return ans;
    }
    // Driver code
    static main(args)
    {
        var Arr = [2, 3, 4, 5];
        var N = Arr.length;
        for (let i=0; i < (1 << 20); i++)
        {
            (GFG.subsets.push(new Array()) > 0);
            GFG.canSplit[i] = false;
        }
        document.write(GFG.UtilCountOfSubsets(N, Arr));
    }
}
GFG.main([]);
 
</script>


Output

2

Time Complexity: O(6N/2). 

  • Generating all subsets from the first and second half of the array takes O(3N/2).
  • In the worst case all the subsets of first half may be matched with the current subset of second half taking O(2N/2).
  • Hence overall time complexity is O(3N/2 * 2N/2) = O(6N/2).

Auxiliary Space: O(2N)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads