假设我有一个N个非负整数的数组,每个非负整数可以非常大(0-> 100,000)。我们也假设N可以非常大(~100,000,000)。
对于数组[a0 a1 ... aN-1],我想编写一个函数,它返回整个数组的(-2)^ ai之和。我想要O(n * log(n))时间复杂度和O(n)空间。
例如,取[1 2 3] - 这将返回(-2)^ 1 +( - 2)^ 2 +( - 2)^ 3 = -6 另一个限制是,对于超过100,000,000的答案,函数应该返回-1;
一个天真(但错误)的解决方案如下:
int solve(vector<int> &A) {
int answer = 0;
for (auto iter = A.begin(); iter != A.end(); ++iter) {
answer += pow(-2, *iter);
}
return (answer <= 1e8) ? answer : -1;
}
这不起作用b / c答案将溢出值&gt; 31(假设本机有符号整数大小为4个字节)。使用long也无法使b / c中断数组中大于63的值。
我能想到的一个高级解决方案是使用std :: sort对数组进行排序,然后再进行处理。对于数组中大于31的值,我们通过从数组中的值减去31来计算出31的倍数。这是可以接受的b / c我们正在处理指数的总和。我很好奇是否有已知的O(n * log(n))复杂度,O(n)空间解决方案来解决这个问题。
答案 0 :(得分:1)
请注意,(-2)^K
改变了简单的二进制表示形式:偶数K为..00001000..
,奇数K为..1111110000..
(2&#39; s补码)。
因此,您可以创建数组(int或boolean)来累积二进制表示中的和。它的长度应该通过数组的最大值来确定(开销取决于N - 关于Log2(N)单元)。
然后遍历数组,只需将当前数字的二进制表示添加到累加器。数组A=[2,3,4]
value(K) binary(-2)^K accum
00000000
2 100 00000100
3 11111000 11111100
4 00010000 00001100
每个添加操作都需要Max(A)+ Log2(N)基本操作
可能的迷你优化 - 对输入数组和组重复值进行排序。例如,如果数组包含8的值4,则可以在单班操作中轻松获取8*(-2)^4= 10000 << 3 = 10000000
,而无需7次添加操作。
答案 1 :(得分:0)
一个想法......
您的功能回答了2个问题:
1)结果是否符合100M限制 2)低于100M的项目总和是多少
如果1)不满意,你不必计算后者,所以最终计数可以更少关心是否适合int。
为了简化操作,我们可以使用计数排序和总和计数。让数组计数将保持2 ^(i)的乘数,因此最终的总和将是sum(counts [i] * 2 ^ i)。不是计数不使用标志触发器,我们必须在填充时添加适当的标志。
现在我们可以减少计数数组。注意,如果计数[i]> 2,那么对于修改如下的数组,sum将是相同的:
同样适用于负号。因此,在从0到最大计数的一个循环中,我们可以减少计数中的值,在每个值中仅留下0和1 / -1。
如您所知,2 ^ N索引的值大于在0..2 ^ N-2值中累积的任何值的总和至少2次。因此,如果您的最高指数(减少后)大于28(2 ^ 28 = 268,435,456),那么结果将不适合100,000,000。
现在,如果1)是pase,你知道最终和临时结果不大于268,435,456,所以它将适合int类型,所以只需做数学并再次检查最终结果。