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‘.
- Base Case, If i == N/2, then the first half of the array has been traversed completely.
- 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)1 = ∑(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.
- 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 :
- 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‘.
- Base Case, If i == N, then the entire array has been traversed completely starting from the middle.
- 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> |
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)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!