给定N个数字,我需要计算其总和为S的子集。
注意:数组中的数字不必相同。
我目前的代码是:
int countSubsets(vector<int> numbers,int sum)
{
vector<int> DP(sum+1);
DP[0]=1;
int currentSum=0;
for(int i=0;i<numbers.size();i++)
{
currentSum+=numbers[i];
for (int j=min(sum,currentSum);j>=numbers[i];j--)
DP[j]+=DP[j - numbers[i]];
}
return DP[sum];
}
他们可以比这更有效吗?
约束是:
1 ≤ N ≤ 14
1 ≤ S ≤ 100000
1 ≤ A[i] ≤ 10000
他们在一个文件中也有100个测试用例。如果他们存在比这个更好的解决方案,请帮助
答案 0 :(得分:1)
N很小(2 ^ 20 - 约为1 milion - 2 ^ 14是非常小的值) - 只是迭代所有子集,下面我写了很快的方法(bithacking)。将整数视为集合(按字典顺序列举子集)
int length = array.Length;
int subsetCount = 0;
for (int i=0; i<(1<<length); ++i)
{
int currentSet = i;
int tempIndex = length-1;
int currentSum = 0;
while (currentSet > 0) // iterate over bits "from the right side"
{
if (currentSet & 1 == 1) // if current bit is "1"
currentSum += array[tempIndex];
currentSet >>= 1;
tempIndex--;
}
subsetCount += (currentSum == targetSum) ? 1 : 0;
}
答案 1 :(得分:0)
您可以使用N
很小的事实:可以生成给定数组的所有可能子集,并检查每个子集的总和是否为S
。时间复杂度为O(N * 2 ** N)
或O(2 ** N)
(取决于生成方式)。对于给定的约束,此解决方案应该足够快。
以下是O(2 ** N)
解决方案的伪代码:
result = 0
void generate(int curPos, int curSum):
if curPos == N:
if curSum == S:
result++
return
// Do not take the current element.
generate(curPos + 1, curSum)
// Take it.
generate(curPos + 1, curSum + numbers[curPos])
generate(0, 0)
基于中间技术满足的更快解决方案:
让我们使用上面描述的算法生成数组前半部分的所有子集,并将它们的总和放入一个映射中(将总和映射到具有它的子集的数量。它可以是一个哈希表或者只是一个数组,因为S
相对较小)。此步骤需要O(2 ** (N / 2))
时间。
现在让我们为后半部分生成所有子集,并为每个子集添加上半部分总和为S - currentSum
e的子集数(使用1中构造的映射),其中currentSum
是当前子索引中所有元素的总和。我们再次提供O(2 ** (N / 2))
个子集,并在O(1)
中处理每个子集。
总时间复杂度为O(2 ** (N / 2))
。
此解决方案的伪代码:
Map<int, int> count = new HashMap<int, int>() // or an array of size S + 1.
result = 0
void generate1(int[] numbers, int pos, int currentSum):
if pos == numbers.length:
count[currentSum]++
return
generate1(numbers, pos + 1, currentSum)
generate1(numbers, pos + 1, currentSum + numbers[pos])
void generate2(int[] numbers, int pos, int currentSum):
if pos == numbers.length:
result += count[S - currentSum]
return
generate2(numbers, pos + 1, currentSum)
generate2(numbers, pos + 1, currentSum + numbers[pos])
generate1(the first half of numbers, 0, 0)
generate2(the second half of numbers, 0, 0)
如果N
为奇数,则中间元素可以转到前半部分或第二部分。只要它恰好属于其中一个,它在哪里都没关系。