📌  相关文章
📜  可以划分为两个总和相等的非空集的子集计数

📅  最后修改于: 2022-05-13 01:56:08.102000             🧑  作者: Mango

可以划分为两个总和相等的非空集的子集计数

给定一个大小为N的数组Arr[] ,任务是找到Arr[]的子集的计数,这些子集可以划分为两个总和相等的非空组。

例子:

朴素方法:最简单的方法是生成所有子集,并且对于每个子集S ,如果它的总和为A ,则找到S的所有子集并检查其总和是否为A/2。

时间复杂度: O(4 N )
辅助空间: O(1)

有效方法:有效解决问题的想法是使用 中间的相遇 技术。

请按照以下步骤解决问题:

  • 声明一个大小为(1 << N)的全局布尔数组 ' canSplit ' 以存储每个子集的结果,如果它可以分成两个总和相等的组。
  • 声明一个全局Map以将子集的总和分配给一个 ID,并查找是否已经存在与当前子集具有相同总和的子集。
  • 声明 Vector 的全局Vector以将具有相同总和的所有位掩码存储在一起。
  • 定义一个递归函数,比如makeSubsets1(i, sum, mask)从数组的前半部分构造所有子集。
    • 基本情况,如果i == N/2 ,则数组的前半部分已被完全遍历。
      • 如果位掩码表示的当前子集的总和与已经形成的子集不同,则将变量ID增加1并将sum 的值分配给该ID
      • 检索当前总和的 ID 号。
      • 将位掩码表示的子集添加到检索到的 ID 的向量中。
    • 当前索引处的数字可以是:
      • 从当前子集中排除
      • 包含到子集的第一组。在这种情况下,我们数字添加到“ sum ”。
      • 包含到子集的第二组。在这种情况下,我们从“ sum ”中减去该数字。
  • 定义一个递归函数,比如makeSubsets2(i, sum, mask)从数组的后半部分构造所有子集。
    • 基本情况,如果i == N ,则整个数组已从中间开始完全遍历。
      • 如果数组前半部分中的任何子集与当前子集的和相同,则两者的按位或形成有效子集,因为它们具有相同的不平衡值。换句话说,如果:
        • ∑(First group) 1 – ∑(Second group) 1 = ∑(Second group) 2 – ∑(First group) 2通过重新排列术语,
        • ∑(第一组) 1 + ∑(第一组) 2 = ∑(第二组) 1 + ∑(第二组) 2
      • 因此,两个子集的按位或可以分为两个总和相等的组。遍历具有相同总和的前半部分的所有子集,并将其与后半部分的当前子集的按位或标记为“”,表示有效子集。
    • 当前索引处的数字可以是:
      • 从当前子集中排除
      • 包含到子集的第二组。在这种情况下,我们数字添加到“ sum ”。
      • 包含到子集的第一组。在这种情况下,我们从“ sum ”中减去该数字。
  • 如果canSplit[mask] = true遍历所有子集并将答案增加1
  • 打印答案。

以下是上述方法的代码:

C++14
// C++ program for the above approach.
 
#include 
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 mp;
 
// Vector of vector to store
// all the bitmasks having the
// same sum together.
vector > 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;
}


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


输出
2

时间复杂度: O(6 N/2 )

  • 从数组的前半部分和后半部分生成所有子集需要 O(3 N/2 )。
  • 在最坏的情况下,前半部分的所有子集都可能与后半部分的当前子集匹配,取 O(2 N/2 )。
  • 因此总体时间复杂度为 O(3 N/2 * 2 N/2 ) = O(6 N/2 )。

辅助空间: O(2 N )