问题如下:
给出N个大小在1到N之间的容器(2 <= N <= 10 ^ 5),每个容器放置在一行中,确定只有在一个容器可以放置在另一个容器中的情况下,才可以释放多少个容器它的大小小于其他大小,并且它们之间没有任何其他容器。可以进行多次放置,因此如果相互放置容器,则可以将它们放置在另一个容器中(如果底部容器的大小小于其他容器的大小),则可以将一个容器放置在另一组容器中(如果容器的底部大小小于顶部容器的大小),并且可以按照相似的规则将一组容器放置在另一组容器中。
示例:如果N = 8,并且容器按以下顺序放置:1 8 2 4 3 6 7 5,则可以释放7个位置;我们将3放在4中,然后将2放在3中,在6放在7中,在5放在6中,在4放在5中,在7放在8中,再将1放在2中。
我解决此问题的想法如下:计算所有相邻容器尺寸之间的最小差异,然后找到出现该最小尺寸的第一对并进行放置。然后再次重复整个过程,直到无法放置为止。然后计算释放的位置。
这可以在二次时间内完成,但这太慢了。令我烦恼的是,这种方法可能不会导致最佳解决方案。
编辑进行澄清:我不想让别人做我的工作,我发布此邮件是因为我坚持解决问题,需要一些帮助来了解我的方法是否正确或我该如何进行优化。另外,解决方案也不是特定于语言的,因此我认为伪代码算法的实现很简单。谢谢,谢谢!
答案 0 :(得分:2)
这是O(n log n)
时间算法。
我们的策略是确定可以打包为一组的容器的最长前缀,然后对其余容器进行递归。要了解为什么这是最佳选择,请首先考虑从实例中删除容器永远不会增加可以保留的最小组数,因为给定了一系列移动,我们只可以删除涉及丢失容器的组。我们的贪婪策略保留了最小的后缀,因此通过强归纳法可以使其达到最佳。
要找到最长的前缀,我们在下面的测试中使用exponential search。总体而言,每次搜索最终将花费O(p log p)
时间,其中p
是最长前缀的长度。汇总所有前缀,总计为O(n log n)
。
给定编号为1...n
的容器,由于Bose,Buss和Lubiw(用于排列的模式匹配,从1998年开始),存在一种线性时间算法来测试是否可以打包为一组。 (作为一个旁注,他们将这些排列命名为separable。)这是一种移位减少算法,该算法保留一堆间隔。要移动容器k
,请将间隔[k, k+1)
推入堆栈。每当堆栈的前两个元素是[a, b), [b, c)
的{{1}}或[b, c), [a, b)
时,将其弹出并推入a, b, c
。最后,当且仅当容器可以打包为一组时,堆栈才只有一个元素。
现在,您可能已经注意到,通常输入的前缀不是 ,编号为[a, c)
。一种简单的方法是每次对被测前缀进行排序和编号,这使测试花费1...n
,并导致O(p log p)
-时间算法。也许这已经足够了。如果不是,请观察O(n log^2 n)
有足够的时间在指数搜索的第一阶段对前缀进行排序(因为成本成倍增加)。此后,我们有一个O(p log p)
的上限u
,并且对于第一个u/2 ≤ p < u
容器有连续的索引。然后,在u
时间里,我们可以计算新索引。
答案 1 :(得分:1)
到目前为止,我能提出的最好的主意是一些想法,也许有人可以更加全面地发展(或指出缺陷)。
从左到右再次迭代,对于索引r
上的每个元素,向后看以寻找最长的序列(l, r)
,我们可以这样做,并注意到在此过程中构造的新范围(下界和上限不一定与索引l
和r
处的元素相对应。如果元素大于上限或小于下限,我们将扩展范围。
请注意,当每个序列向后(向左)扩展时,下界和下界分别单调减小和增大,这意味着可以根据需要通过二元搜索来寻找它们。
i: 0 1 2 3 4 5 6 7
1 8 2 4 3 6 7 5
l is displayed as (index, range)
index r
0 -> [(0, (0,0))]
1 -> [(0, (1,8))]
2 ->
[(1, (2,8)), (0, (1,8))]
3 ->
[(2, (2,4)), (1, (2,8)),
... repeated]
4 ->
[(3, (3,4)), (2, (2,4)),
... repeated]
5 ->
(4, (3,6)) cannot extend back
to element 4 at index 3, but
element 6 can join earlier,
reachable ranges.
[(4, (3,6)), (3, (3,6)),
(2, (2,6)), (1, (2,8)),
... repeated]
6 ->
[(5, (6,7)), (4, (3,7)),
(3, (3,7)), (2, (2,7)),
(1, (2,8)) ... repeated]
7 ->
[(6, (5,7)), (5, (5,7)),
(3, (3,7)) ... repeated)
我们可以看到,在向后搜索期间,一旦可以将元素插入到与左数组索引相关联的先前看到的间隔中,我们就可以查找将其向左扩展的最佳关联解决方案。否则,我们将使用该索引之前未看到的新间隔进行更新。
这看起来像O(n^2)
,因为每个子数组都可能指向关联的元素范围。但是,任何指向特定范围的左数组索引只有一个最佳序列向左延伸,无论它属于多少个子数组。 (在示例中,我们看到了13个唯一的(left, (low,high))
,而(n-1)*n/2 = 28
)。我的希望是,这表明有些优化。