计算总和为 x 的所有整数子集(包括负数)
count all subsets of integers (including negative) that sum to x
我得到一个最大长度为 40 的数组,其中包含 [-10^9;10^9] 中的整数。我还得到了位于 [0;10^9] 中的预期总和 X。现在我应该给出总和为 X 的子集总数。我想不出一种方法来处理负数。我首先尝试了蛮力方法,它适用于小型输入数组但不适用于大型输入数组:
auto r = 0ULL;
for(auto i = 0ULL; i < (1ULL << n) - 1ULL; ++i) {
auto x = 0ULL;
for(auto j = 0ULL; j < n; ++j) {
if(i & (1ULL << j)) {
x += values[j];
}
}
r += x == sum;
}
cout << r << '\n';
然后我试着记住加法的中间结果,这样我就不必为相同的元素计算多次。但这会消耗 space 的 ALOT(对于长度为 40 x 的输入,应该约为 9 TB))。有人可以给我一个更好的方法的提示吗?
提示:您可以使用循环。设 num_subsets[i][k]
为 values[0], values[1], ..., values[i]
的子集数,总和为 k
。那么
num_subsets[i][k] = num_subsets[i - 1][k – values[i]] + num_subsets[i - 1][k]
那么num_subsets[n-1][x]
就是你的答案。
由于内存原因,总和范围和项目计数对于动态规划来说看起来太大了。
但 40 个项目的限制允许应用类似 "meet-in-the-middle" 原则。
分离前 20 个项目并计算所有可能子集的总和 - 只需遍历 2^20 个子集 (包括空子集!)(使用子集编号的递归或二进制表示或其他方式)并在包含对 (sum, count)
.
的地图中写入总和
下半场同样如此。请注意,地图的大小非常可靠。
遍历第一个地图键并从第二个地图中寻找相应的键。伪代码:
for sum in map1:
if map2.contains(X - sum):
result += map1[sum] * map2[X - sum]
我得到一个最大长度为 40 的数组,其中包含 [-10^9;10^9] 中的整数。我还得到了位于 [0;10^9] 中的预期总和 X。现在我应该给出总和为 X 的子集总数。我想不出一种方法来处理负数。我首先尝试了蛮力方法,它适用于小型输入数组但不适用于大型输入数组:
auto r = 0ULL;
for(auto i = 0ULL; i < (1ULL << n) - 1ULL; ++i) {
auto x = 0ULL;
for(auto j = 0ULL; j < n; ++j) {
if(i & (1ULL << j)) {
x += values[j];
}
}
r += x == sum;
}
cout << r << '\n';
然后我试着记住加法的中间结果,这样我就不必为相同的元素计算多次。但这会消耗 space 的 ALOT(对于长度为 40 x 的输入,应该约为 9 TB))。有人可以给我一个更好的方法的提示吗?
提示:您可以使用循环。设 num_subsets[i][k]
为 values[0], values[1], ..., values[i]
的子集数,总和为 k
。那么
num_subsets[i][k] = num_subsets[i - 1][k – values[i]] + num_subsets[i - 1][k]
那么num_subsets[n-1][x]
就是你的答案。
由于内存原因,总和范围和项目计数对于动态规划来说看起来太大了。
但 40 个项目的限制允许应用类似 "meet-in-the-middle" 原则。
分离前 20 个项目并计算所有可能子集的总和 - 只需遍历 2^20 个子集 (包括空子集!)(使用子集编号的递归或二进制表示或其他方式)并在包含对 (sum, count)
.
下半场同样如此。请注意,地图的大小非常可靠。
遍历第一个地图键并从第二个地图中寻找相应的键。伪代码:
for sum in map1:
if map2.contains(X - sum):
result += map1[sum] * map2[X - sum]