我正在为即将来临的考试而学习,并且正在练习一个问题,希望我实施贪婪算法。
为我提供了不同权重的未排序数组,其中所有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)?如果有,我只是没有看到它,需要一些帮助。
答案 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)