查找元素小于S的子集

时间:2019-02-27 12:51:44

标签: algorithm set time-complexity subset-sum

我必须找到一种解决以下问题的算法:

输入是两个自然数S和k,以及一组n对成对的不同数的无序集合。

确定O(n)是否有k个数字的子集加起来等于<= S。注意:k不应成为时间复杂度的一部分。

algorithm({x_1, ..., x_n}, k, S):
    if exists |{x_i, ..., x_j}| = k and x_i + ... x_j <= S return true

我找不到时间复杂度为O(n)的解决方案。

我能得到的是O(kn),因为我们搜索k倍最小值并求和:

algorithm(a={x_1, ..., x_n}, k, S):
    sum = 0
    for i=1,...,k:
        min = a.popFirst()
        for i=2,...,len(a):
            if(a[i] < min):
                t = a[i]
                a[i] = min
                min = t
        sum += min
    if sum <= S:
        return true
    else:
        return false

这在O(n)中,并返回正确的结果。我怎样才能松开k?

感谢您的帮助,我真的很努力!

2 个答案:

答案 0 :(得分:3)

快速选择可用于找到 k 个最小元素:https://en.wikipedia.org/wiki/Quickselect

基本上,它是快速排序,只是您只在轴的有趣一侧进行递归。

一个简单的实现可以在 O(N) 预期时间内运行,但是使用中位数中位数来选择枢轴,则可以使情况变得更糟绑定:https://en.wikipedia.org/wiki/Median_of_medians

答案 1 :(得分:2)

您可以根据集合构建大小为k的最小堆。建立时间的复杂度是O(n)个预期时间,O(n log k)个最坏的情况。 堆应包含集合中的前k个最小元素。

然后可以很容易地看到堆中元素的总和为<= S。您无需从堆中删除元素即可计算总和。只需遍历堆以计算总和。删除所有元素会带来k log k的复杂性。

您甚至不需要考虑下一个较高的元素,因为相加会导致总和大于S