如何选择最少数量的权重以获得O(n)时间内的总权重

时间:2016-09-13 15:26:03

标签: algorithm

如果有n未排序的权重,我需要找到最少数量的权重才能获得至少权重W。 如何在O(n)中找到它们?

1 个答案:

答案 0 :(得分:3)

此问题有许多解决方法:

方法1 - 排序 - O(nlogn)
我想最简单的一个是按降序排序,然后取第一个K元素,它们总和至少为W.时间复杂度虽然是O(nlogn)

方法2 - 最大堆 - O(n + klogn)
另一种方法是使用最大堆。 创建堆将采用O(n)然后提取元素,直到我们达到至少为W的总和。每次提取将花费O(logn),因此总时间复杂度为O(klogn)其中k 1}}是我们必须从堆中提取的元素数。

方法3 - 使用Min Heap - O(nlogk)
添加JimMischel在以下评论中建议的此方法 创建一个最小堆,其中列表中的第一个k元素总和至少为W。然后,迭代剩余的元素,如果它大于它们之间的最小值(堆顶部)替换 在这一点上,我们可能需要更多的元素来实现W,所以我们只需要提取最小值,直到达到极限。在实践中,取决于

之间的关系
find_min_set(A,W)
    currentW = 0
    heap H //Create empty heap

    for each Elem in A
        if (currentW < W)
            H.add(Elem)
            currentW += Elem
        else if (Elem > H.top())
            currentW += (Elem-H.top())
            H.pop()
            H.add(Elem)

    while (currentW-H.top() > W)
        currentW -= H.top()
        H.pop()

此方法在实践中可能更快,具体取决于kn之间的关系。请参阅when theory meets practice

方法4 - O(n)
我能想到的最好的方法是使用某种quickselect,同时跟踪总重量,并始终将中位数作为支点进行划分。

首先,让我们定义一些事情:

sum(A) - 数组A中所有元素的总和。
num(A) - 数组A中的元素数量 med(A) - 数组A的中位数。

find_min_set(A,W,T)
    //partition A
    //L contains all the elements of A that are less than med(A)
    //R contains all the elements of A that are greater or equal to med(A)
    L, R = partition(A,med(A))
    if (sum(R)==W)
        return T+num(R)
    if (sum(R) > W)
        return find_min_set(R,W,T)
    if (sum(R) < W)
        return find_min_set(L,W-sum(R),num(R)+T)

通过find_min_set(A,W,0)调用此方法。

运行时复杂性:

  • 查找中位数为O(n)
  • 分区为O(n)
  • 每次递归调用占用数组的一半大小。
  • 总结一下,我们得到一个关注关系:T(n) = T(n/2) + O(n),与quickselect = O(n)的平均情况相同。

注意:当所有值都是唯一的时,最坏情况和平均复杂度确实都是O(n)。对于可能的重复值,平均复杂度仍为O(n),但最糟糕的情况是O(nlogn),使用Median of medians方法选择数据透视。