找到一组子数组的总和

时间:2016-07-01 19:56:51

标签: c++ algorithm math

这是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个项目的大型数据集,似乎任何预先计算整个子阵列数组的解决方案都太昂贵了。所有提交的解决方案似乎都不能计算整个子阵列

4 个答案:

答案 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 + 1start + 1,然后从start + 1start + 2,然后从start + 1start + 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))