总和在 A 和 B 之间的子集
给定一组 N 个整数。找出给定数组的多少子集在 A 和 B(包括)之间的总和。
Constraints:
1 ≤ N ≤ 34,
-2 * 107 ≤ arri ≤ 2 * 107
-5 * 108 ≤ A, B ≤ 5 * 108
例子:
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] 的上限
(让我们说“高”)。减去(高 - 低)以获得所需的答案。
For e.g S = { 1, 2, -1, 0 }, A = 1, B = -1.
After dividing S into two sets, S1 = { 1, 2 } and S2 = { -1, 0 }.
Power set of S1 = { {0}, {1}, {2}, {1, 2} } and Power set of S2 = { {0}, {-1}, {0}, {-1, 0} }
Subset Sum of S1 = { 0, 1, 2, 3 } and Subset Sum of S2 = { 0, -1, 0, -1 }
Now sort the S2 { -1, -1, 0, 0 } and for every value in S1, we binary search values that would yield the desired sum. For 0 we search for (-1) – 0 = -1 for lower bound and 1 – 0 = 1 for upper bound in S2, for 1 we search for (-1) – 1 = -2 and 1 – 1 = 0 in S2 and so on.
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 是集合的大小。