这是2017年Google APAC提出的问题。 Problem D: Sum of Sum
Alice向她的朋友Bob展示了一组N个正整数,从1到N的索引。她向Bob提出了很多关于表格的查询"这两个索引之间的数字总和是多少?&#34 ;但鲍勃能够轻松解决问题。爱丽丝拿走了她的阵列,发现了所有N *(N + 1)/ 2个非空子阵列。她找到了每个子数组的总和,然后对这些值进行排序(以非递减顺序)以创建一个新数组,索引范围为1到N *(N + 1)/ 2。例如,对于初始数组[2,3,2],Alice将生成子数组[2],[3],[2],[2,3],[3,2]和[2,3], 2](注意,[2,2],例如,不是一个子阵列)。然后她取出总和 - 2,3,2,5,5,7 - 然后对它们进行排序以获得[2,2,3,5,5,7]的新数组.Alice给出了鲍勃的初始数组,以及形式的Q查询"新数组中从索引Li到Ri的数字总和是多少?"现在鲍勃遇到了麻烦!你能帮帮他吗?
即使在大数据集的c ++中,直接解决方案也效率太低。有没有更有效的方法来解决这个问题?
目前我正在通过这个for循环来构建最终数组:
multiset<int> sums;
long long int temp = 0;
for (long long int len = 1; len <= n; ++len)
{
for (int start = 0; start+len <= n; ++start)
{
temp = 0;
for (int i = 0; i < len; ++i)
{
temp += arr[start + i]; //arr stores the original array of n digits
}
sums.insert(temp);
}
}
P.S:我当前的实现是O(n ^ 5)?我的错误,我现在可以看到它的O(n ^ 3)。谢谢
编辑:答案到目前为止一直很有帮助,但是对于涉及n = 200000个项目的大型数据集,似乎任何预先计算整个子阵列数组的解决方案都太昂贵了。所有提交的解决方案似乎都不能计算整个子阵列
答案 0 :(得分:3)
如评论中所述,您的解决方案是O(N ^ 3),计算为O(N ^ 2)乘以O(N)和以及多集中的插入(与O(N)相比可以忽略,见这个答案的底部)。
但交换你的两个第一个循环,你正在做完全相同的N *(N + 1)/ 2总和和插入:
for (int start = 0; start < n; ++start)
{
for (long long int len = 1; start + len <= n; ++len)
{
temp = 0;
for (int i = 0; i < len; ++i)
{
temp += arr[start + i]; //arr stores the original array of n digits
}
sums.insert(temp);
}
}
现在,如果你查看你的temp
总和,你似乎很明显你正在做多余的工作。从start + 1
到start + 1
,然后从start + 1
到start + 2
,然后从start + 1
到start + 3
等等。对于每个新{{1}你正在计算的总和是前一个len
值加上一个项目的总和。因此,您可以删除此内循环:
len
所以在N *(N + 1)/ 2中你生成了一组值。当然,使用多重集可以隐藏数据排序,但插入通常需要花费for (int start = 0; start < n; ++start)
{
temp = 0;
for (long long int len = 1; start + len <= n; ++len)
{
temp += arr[start + len]; //arr stores the original array of n digits
sums.insert(temp);
}
}
。
单独排序,因为对一组大小S进行排序应该采用S * log(S),费用log(sums.size())
({只)小于N*(N+1)/2 * log ( N*(N+1)/2 )
。
请注意,由于您有正整数,因此您使用内循环生成的每组N*(N+1) * log((N+1)/sqrt(2))
整数已经排序,因此您可以使用它们来执行智能操作以加快排序。这也是多重集团according to cplusplus.com的作用:
如果插入N个元素,一般是Nlog(大小+ N),但如果元素已根据容器使用的相同排序标准排序,则大小为线性+ N.
答案 1 :(得分:1)
做一点搜索我发现了这个,我希望它会有用
答案 2 :(得分:0)
我能想到的最简洁有效的方法是:
std::vector<int> in{ 2, 3, 2 };
std::vector<int> out(in.size()*(in.size()+1)/2);
auto out_it = out.begin();
for (size_t i = 0; i < in.size() ; ++i) {
out_it=std::partial_sum(in.begin()+i, in.end(), out_it);
}
std::sort(out.begin(), out.end());
除了复杂性考虑(这个解决方案是 - 我相信 - O(n ^ 2 * log(n)),因为你正在使用O(n ^ 2)条目对数组进行排序),你应该避免动态内存分配和指针追逐像瘟疫一样(两者都是std::multi_set
的固有部分)。
答案 3 :(得分:0)
我的Python 3版本在下面。
我并没有测试所有测试用例,但这只是一个实现的想法:
from functools import reduce
import itertools
stuff = [2,3,2]
temp = []
for L in range(1, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
if len(subset) > 1:
if all(subset[0] == x for x in subset):
continue
print(subset)
temp.append(reduce(lambda x,y: x+y, subset))
temp = sorted(temp)
print(temp)
print("all sum : ", reduce(lambda x,y: x+y, temp))