一位朋友给了我一个谜题,他说可以比O(n ^ 3)时间更好地解决。
给定一组n个作业,每个作业都有一个设定的开始时间和结束时间(重叠是非常可能的),找到每个作业包含该作业的最小子集,或者包括与该作业重叠的作业。 / p>
我非常确定最佳解决方案是选择具有最多未标记重叠的作业,将其添加到解决方案集,然后标记它及其重叠。并重复,直到所有工作都被标记 确定哪个作业具有最多未标记的重叠是一个简单的邻接矩阵(O(n ^ 2)),每次选择作业时都必须重做,以便更新标记,使其成为O(n ^ 3) )。
有更好的解决方案吗?
答案 0 :(得分:9)
让A
成为我们尚未重叠的作业集。
x
中找到具有最小结束时间(A
)的作业t
。t
的所有工作:选择具有最长结束时间的工作j
。j
添加到输出集。j
删除所有与A
重叠的作业。A
为空。一个简单的实现将在O(n ^ 2)中运行。使用interval trees,可能可以在O(n * logn)中解决。
为什么它是最佳解决方案(不是正式证明)背后的基本思想:我们必须选择一个开始时间小于t
的工作,以便x
重叠。如果我们让S
成为开始时间小于t
的所有作业的集合,则可以显示j
将与S
中的任何作业重叠相同的作业,加上可能更多。由于我们必须在S
中选择一项工作,因此最佳选择是j
。我们可以利用这个想法通过对工作数量的归纳来形成证据。
答案 1 :(得分:1)
我们可以使用动态编程方法实现O(nlogn)解决方案。特别是,我们要考虑最小集合的大小,包括k th 作业,并匹配第一个k
作业(按开始时间排序),我们用{{1}表示}。我们首先应该添加一个辅助作业(∞,∞),因此结果将是我们最终作业的DP解决方案减去一个。
要计算S(k)
,请考虑作业S(k)
,该作业在作业p(k)
之前结束,但具有最长的开始时间。请注意k
是一个增加的函数。 p
将S(k)
与S(i)
最低end(i) > start(p(k))
。
我们可以通过维持(S(k)
有序最小)潜在工作堆来有效地找到这份工作。在计算每个S(k)
之后,我们将作业添加到堆中。当我们想要找到一份工作时,我们会在堆的基础上删除过早结束的工作,直到找到合适的工作。这将最多花费O(nlogn),因为我们最多只执行每个堆操作的O(n)(pop / peek / push)。
剩下的任务是有效地计算p(k)
值。一种方法是迭代所有工作的开始和结束(以增加的时间),跟踪最新的起始工作。