这种贪婪算法能更有效吗?

时间:2019-09-18 20:59:13

标签: algorithm performance time greedy

我正在为即将来临的考试而学习,并且正在练习一个问题,希望我实施贪婪算法。

为我提供了不同权重的未排序数组,其中所有i的0 不将两个重物放在一堆中,其中一个重物大于下一个重物。我还必须尊重权重的排序,因此必须按顺序放置它们。堆没有高度限制。

一个例子:如果我有权重{53,21,40,10,18}我不能将40放在21之上,因为桩必须降序排列,而我也不能将21放在40之上,因为那不遵守订购。最佳解决方案将是桩1:53、21、10和桩2:40 18

我的一般解决方案是遍历数组,并总是选择允许重的第一堆。我相信这将为我提供最佳解决方案(尽管我尚未证明)。我找不到与此相反的例子。但这将是O(n ^ 2),因为最坏的情况是我必须遍历每个元素和每个堆(我认为)

我的问题是,有没有办法将其降低到O(n)或O(nlogn)?如果有,我只是没有看到它,需要一些帮助。

2 个答案:

答案 0 :(得分:3)

您的算法将给出正确的结果。

现在请注意以下几点:当按顺序访问堆并停在可以堆叠下一个值的第一个堆时,您总是会遇到这样的情况,即堆按其当前的最高(最后)值升序排列。订单。

您可以使用此属性来避免桩从“左到右”的迭代。而是在堆中使用binary search来查找可以采用下一个值的第一个堆。

这会给您 O(nlogn)时间复杂度。

答案 1 :(得分:2)

信不信由你,你描述的问题等同于计算最长增长子序列的长度。关于为什么有一个整洁的贪婪的小想法。

考虑阵列中最长的递增子序列(LIS)。因为元素在索引上同时在价值上也在上升,所以它们必须全部位于不同的堆中。结果,所需的最小桩数等于LIS中的单元数。

通过动态编程和二进制搜索,LIS可以在O(NlogN)中轻松解决。

请注意,您描述的算法与以下算法具有相同的功能-它找到您可以放置​​该项目的第一堆(通过二进制搜索),或者创建一个新的堆,因此这可以作为“证明”算法的正确性以及降低复杂性的方法。

dp[i]等于长度(i + 1)递增子序列末尾的最小值元素。要根据您的问题重新构造,dp[i]也将等于ith堆上石头的重量。

from bisect import bisect_left
def lengthOfLIS(nums):

    arr = []

    for i in range(len(nums)):
        idx = bisect_left(arr, nums[i])
        if idx == len(arr):
            arr.append(nums[i])
        else:
            arr[idx] = nums[i]
    return len(arr)