📜  总和在 A 和 B 之间的子集

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

总和在 A 和 B 之间的子集

给定一组 N 个整数。找出给定数组的多少子集在 A 和 B(包括)之间的总和。

例子:

Input : S[] = { 1, -2, 3 }, A = -1, B = 2
Output : 5

Explanation:
1) 0 = 0 (the empty subset)
2) {1} = 1
3) {1, -2} = 1 + (-2) = -1
4) {-2, 3} = (-2) + 3 = 1
5) {1, -2, 3} = 1 + (-2) + 3 = 2

方法1(蛮力) :我们可以生成给定数字的所有子集,即Power Set,并找到可以在A和B之间求和的子集的数量。但这最多有2 34次操作,效率不高。因此,以下是解决此问题的有效方法。
方法2(在中间相遇 :这基本上将时间复杂度从 O(2 N ) 降低到 O(2 N/2 )
我们将集合分为两个集合 [0…N/2] 和 [(N/2 + 1)…(N-1)],并为这两个集合分别生成所有子集和,这将是 2 * 2 17次操作。现在,我们可以做的是找到这些集合的组合,它们会给出所需的总和。这又可以以一种有效的方式完成,对一个求和的集合进行排序,然后对将产生另一个集合的特定值的总和的值进行二分搜索。对第二组进行排序,对于第一组中的每个元素,搜索 A – S2[i] 的下限(假设为“低”)和 B – S2[i] 的上限
(让我们说“高”)。减去(高 - 低)以获得所需的答案。

C++
// C++ program to find the Number of Subsets that
// have sum between A and B
#include 
using namespace std;
 
/* Function to Generate all subsets of a set
   start --> Starting Index of the Set for the
             first/second half Set
   setSize --> Number of element in half Set
   S --> Original Complete Set
   res --> Store the subsets sums */
void generateSubsets(int start, int setSize, int S[],
                                    vector& res)
{
    // setSize of power set of a set with setSize
    // N is (2^n - 1)
    unsigned int pow_setSize = pow(2, setSize);
 
    // Store the sum of particular subset of set
    int sum;
 
    // Run from counter 000..0 to 111..1
    for (int counter = 0; counter < pow_setSize; counter++) {
 
        // set the sum initially to zero
        sum = 0;
 
        for (int j = 0; j < setSize; j++) {
 
            // Check if jth bit in the counter is set
            // If set then print jth element from set
            if (counter & (1 << j))
                sum += S[j + start];
        }
 
        // Store the sum in a vector
        res.push_back(sum);
    }
}
 
int numberOfSubsets(int S[], int N, int A, int B)
{
    // Vectors to store the subsets sums
    // of two half sets individually
    vector S1, S2;
 
    // Generate subset sums for the first half set
    generateSubsets(0, N / 2, S, S1);
 
    // Generate subset sums for the second half set
    if (N % 2 != 0)
        generateSubsets(N / 2, N / 2 + 1, S, S2);
    else
        generateSubsets(N / 2, N / 2, S, S2);
 
    // Sort the second half set
    sort(S2.begin(), S2.end());
 
    // Vector Iterator for S1 and S2;
    vector::iterator low, high;
 
    // number of required subsets with desired Sum
    int ans = 0;
 
    for (int i = 0; i < S1.size(); i++) {
 
        // search for lower bound
        low = lower_bound(S2.begin(), S2.end(), A - S1[i]);
 
        // search for upper bound
        high = upper_bound(S2.begin(), S2.end(), B - S1[i]);
 
        // Add up to get the desired answer
        ans += (high - low);
    }
    return ans;
}
 
// Driver Program to test above functions
int main()
{
    int S[] = { 1, -2, 3 };
    int N = sizeof(S) / sizeof(S[0]);
 
    int A = -1, B = 2;
 
    // Find the number of subsets with desired Sum
    cout << numberOfSubsets(S, N, A, B) << endl;
    return 0;
}


Java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.function.BiPredicate;
 
// Java program to find the Number of Subsets that
// have sum between A and B
public class Main
{
   
    /* Function to Generate all subsets of a set
    start --> Starting Index of the Set for the
             first/second half Set
    setSize --> Number of element in half Set
    S --> Original Complete Set
    res --> Store the subsets sums */
    private static void generateSubsetSumsRecur(int[] arr, int st, int end, int index, int runningSum, List sums) {
        if (index == end+1) {
            sums.add(runningSum);
            return;
        }
        generateSubsetSumsRecur(arr, st, end, index+1, runningSum+arr[index], sums);
        generateSubsetSumsRecur(arr, st, end, index+1, runningSum, sums);
    }
    private static long numberOfSubsets(int arr[],int n, int a, int b) {
        // Generate subset sums for the first half set
        List sums = new ArrayList<>();
        generateSubsetSumsRecur(arr, 0, n/2, 0, 0, sums);
        Integer[] firstSubsetSums= sums.toArray(new Integer[0]);
     
        // Generate subset sums for the second half set
        List sums2 = new ArrayList<>();
        generateSubsetSumsRecur(arr, n/2+1, n-1, n/2+1, 0, sums2);
        Integer[] secondSubsetSums= sums2.toArray(new Integer[0]);
         
        // Sort the second half set
        Arrays.sort(secondSubsetSums);
         
        long count = 0;
        for(int i=0; isum>=mark);
            int q = findLastIdxWithFalsePredicate(secondSubsetSums,
                                                  b-firstSubsetSums[i],
                                                  (sum, mark)->sum>mark);
            count += (q-p);
        }
        return count;
    }
    private static int findLastIdxWithFalsePredicate(Integer[] sums,
                                                     int val,
                                                     BiPredicate pred) {
        int min = 0;
        int max = sums.length-1;
        while (min


Python3
# Python3 program to find the number of
# subsets that have sum between A and B
 
# Module for Bisection algorithms
import bisect
 
'''
Function to Generate all subsets of a set
    start --> Starting Index of the Set for the
        first/second half Set
    setSize --> Number of element in half Set
    S --> Original Complete Set
    res --> Store the subsets sums
'''
def generateSubsets(start, setSize, S, res):
     
    # setSize of power set of a set with setSize
    # N is (2^n - 1)
    pow_setSize = pow(2, setSize)
 
    # Store the sum of particular subset of set
    add = 0
 
    # Run from counter 000..0 to 111..1
    for counter in range(pow_setSize):
         
        # set the sum initially to zero
        add = 0
 
        for j in range(setSize):
             
            # Check if jth bit in the counter is set
            # If set then print jth element from set
            if counter & (1 << j):
                add += S[j + start]
 
        # Store the sum in a vector
        res.append(add)
         
def numberOfSubsets(S, N, A, B):
 
    # Vectors to store the subsets sums
    # of two half sets individually
    S1 = []
    S2 = []
 
    # Generate subset sums for the first half set
    generateSubsets(0, N // 2, S, S1)
 
    # Generate subset sums for the second half set
    if (N % 2 != 0):
        generateSubsets(N // 2,
                        N // 2 + 1, S, S2)
    else:
        generateSubsets(N // 2,
                        N // 2, S, S2)
 
    # Sort the second half set
    S2.sort()
 
    # Number of required subsets
    # with desired Sum
    ans = 0
 
    for i in range(len(S1)):
 
        # Search for lower bound
        low = bisect.bisect_left(S2, A - S1[i])
 
        # Search for upper bound
        high = bisect.bisect_right(S2, B - S1[i])
 
        # Add up to get the desired answer
        ans += (high - low)
 
    return ans
 
# Driver code
if __name__=="__main__":
 
    S = [ 1, -2, 3 ]
    N = len(S)
 
    A = -1
    B = 2
 
    # Find the number of subsets
    # with desired Sum
    print(numberOfSubsets(S, N, A, B))
     
# This code is contributed by vinaylingam


输出:
5

时间复杂度: O(2 * 2 N/2 ),其中 N 是集合的大小。