【问题标题】:How to choose the least number of weights to get a total weight in O(n) time如何在 O(n) 时间内选择最少的权重来获得总权重
【发布时间】:2017-01-21 06:35:48
【问题描述】:

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

【问题讨论】:

  • 权重有限制吗?
  • 请提供示例数据 - 这样我们就可以检查它。该数据的示例输出。您是否尝试编写解决方案?
  • @RyanVincent 我有一个有效的 O(n) 算法,但 O(n log n) 绝对是微不足道的。只需对它们进行排序并取出最重的,直到你达到 W。很容易证明任何较小的数字都不是解决方案。
  • @errorist 分区时,跟踪中位数右侧的总重量。然后根据总数是大于还是小于W来划分右半部分或左部分。

标签: algorithm


【解决方案1】:

这个问题有很多解决方法:

方法 1 - 排序 - O(nlogn)
我想最简单的方法是按降序排序,然后取出总和至少为 W 的前 K 个元素。时间复杂度将是 O(nlogn)

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

方法 3 - 使用最小堆 - O(nlogk)
添加JimMischel 在下面的 cmets 中建议的此方法。
使用列表中的第一个 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) - 数组@987654342的中位数@。

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 方法选择枢轴。

【讨论】:

  • @trincot 如果您正在寻找重量完全 W的套装,那将是正确的,但如果您只是想要至少 @ 987654353@,贪婪地选择最大的剩余元素就是你想要的。方法1和2都可以;方法 3 有效,但我认为它的运行时间不会比方法 2 更受限制。
  • 啊,是的,@chepner,我现在看到问题的标题有些误导。我认为它的意思是完全,事实证明这不是所要求的。感谢您的澄清。
  • @chepner 我认为是的。选择中位数是 O(n),分区是 O(n)。通过选择中位数,它保证每个递归都以数组大小的一半调用,这意味着它保证了快速选择的平均情况的运行时间为 O(n)。我错过了什么吗?
  • 只有在你有 unique 元素的情况下才对中位数进行分区。快速排序和快速选择都需要选择一个随机枢轴元素以避免最坏情况的行为;我怀疑这也可能。
  • 堆方法可以在O(k + n log k)中完成。无需创建所有项目的堆。只需创建前 k 个项目的最小堆。然后对于列表中的每个剩余项,如果列表项大于最小项,则替换堆中的最小项。如果 k 与 n 相比非常小,这可能比 O(n) 方法快很多。如果 k 小于 n 的 1%,那么堆选择可能会比快速选择更快。例如,从 100,000 中挑选前 100 名。见blog.mischel.com/2011/10/25/when-theory-meets-practice
猜你喜欢
  • 1970-01-01
  • 2018-10-20
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 2011-04-29
  • 2014-02-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多