我们假设我们有n个时间表。每个时间表i具有权重w(i),准备时间prep(i)和休息时间rest(i)。
准备时间意味着如果我们选择时间表i,我们有义务不选择时间表i - prep(i),i - prep(i)+ 1 ... i-1。
休息时间意味着如果我们选择shedule i,我们有义务不选择shedules i + 1,i + 2 ... i + rest(i)
我们的任务是根据上述限制选择合适的时间表,以便最大化W。
注意:对于i = 1,我们忽略prep(i)。我们假设我们已经准备好了。对于i = n,忽略休息(i)。
限制:一个时间表的准备时间可能与另一个时间表的休息时间重叠。举个例子,如果我们有rest(5)= 2,prep(8)= 2,我们可以选择两个时间表。 rest(5)= 2表示如果我们选择5,则不允许选择6和7。 Prep(8)= 2表示如果我们选择8,则不允许选择6和7。所以我们可以选择5和8。
此任务最合适的算法是什么?
如果不是限制,我们可以说每个时间表都有开始时间i - prep(i)和结束时间i + rest(i)。我们会有一个加权活动选择问题,所以我们可以用贪心算法得到一个最优的O(nlogn)。但是限制破坏了我的计划。
答案 0 :(得分:1)
让f(i)
成为最佳答案,以便i
- 活动是最后一个。如果j
和i
,我们可以从活动j < prep(i)
转到活动j + rest(j) < i
。换句话说,f(i) = (max of f(j) among all valid j such that j < prep(i) and j + rest(j) < i) + 1
。这个公式导致了一个简单的O(N^2)
解决方案。
但我们可以做得更好!让我们保持一个持久的分段树以获得最大的操作(数组中每个位置的一个版本)。最初,它充满了零。对于固定的i
,我们转到prep(i) - 1
版本并在[0, i - 1]
范围内执行最大查询。然后f(i)
是此最大值加1的值。之后,我们使用i + rest(i)
更新位置f(i)
中的树(即创建新版本)。就是这样。
我们O(N)
获得最大值并将一个元素查询更新为持久段树,因此解决方案需要O(N log N)
时间和空间,这看起来非常好。但是,它似乎相当复杂。
现在让我们摆脱持久性。我们可以保持“正常”(即非持久性细分树),并在我们到达i
之后用f(i)
更新位置j = i + rest(i)
中的值(通过保留,让我们说,在每个位置添加的元素向量)。我们不再需要关心第二个限制了。因此,f(i)
是[0, prep(i) - 1]
范围内的最大值加1。找到f(i)
后,我们会将i + rest(i)
位置添加到要添加的向量中。
它仍然使用O(N log N)
时间,但空间复杂度现在是线性的,我们不再需要持久的分段树(事实上,每次更新只能增加值,我们需要一个最大值前缀,所以我们可以在这里使用二进制索引树而不是段树。