书库排序


21

堆放书籍时,通常需要将最大的书籍放在底部,最小的书籍放在顶部。但是,如果我有两本书,其中一本短(高)但比另一本宽,那么我潜在的OCD会让我感到非常不安。无论我按什么顺序放置它们,顶层的书都会在一侧超出底层的书。

例如,假设一本书具有尺寸(10,15),另一本书具有尺寸(11,14)。不管我以哪种方式放置它们,我都会伸出来。但是,如果我有尺寸为(4,3)和的书(5,6),则可以通过将书本放在书本下面来避免悬垂。

出于此挑战的目的,我们将仅考虑与紧随其后的书有关的突出部分。例如,如果我有一个堆栈(5,5)(3,3)(4,4)(没有任何理智的人会这么做),顶书算是悬垂,虽然它不超出底部的书。同样地,堆栈(3,3)(3,3)(4,4)也只有一个悬,尽管超出底部一个顶部的书。

挑战

给定用于书籍尺寸的整数对列表,请对这些对/书籍进行排序,以使突出的数量最少。您不能旋转书本-我希望所有的脊椎都朝向相同的方向。如果存在多个具有相同悬垂数的解决方案,则可以选择任何此类顺序。您的排序算法不一定是稳定的。您的实现可能假设每本书的尺寸小于2 16

时间复杂度:为了使这一点更有趣,算法的渐近最坏情况复杂度必须是堆栈大小的多项式。因此,您不能仅仅测试所有可能的排列。请提供算法最佳性和复杂性的简短证明,并提供一个可选的图表,以显示大型随机输入的比例。当然,您不能使用输入的最大大小作为代码在O(1)中运行的参数。

您可以编写程序或函数,通过STDIN,ARGV或函数参数以任何方便的(未经预处理的)列表格式进行输入,然后打印或返回结果。

这是代码高尔夫球,因此最短的答案(以字节为单位)获胜。

我相信存在多项式解,但是如果您可以证明我错了,则可以提交这样的证明,而不是打高尔夫球。在这种情况下,您可以假设P≠NP。我将接受第一个正确的此类证明,并给予悬赏。

例子

In:  [[1, 1], [10, 10], [4, 5], [7, 5], [7, 7], [10, 10], [9, 8], [7, 5], [7, 5], [3, 1]]
Out: [[10, 10], [10, 10], [9, 8], [7, 7], [7, 5], [7, 5], [7, 5], [4, 5], [3, 1], [1, 1]]

In:  [[4, 5], [5, 4], [5, 4], [5, 4], [5, 4], [4, 5], [4, 5], [4, 5], [5, 4], [4, 5]]
Out: [[4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [5, 4], [5, 4], [5, 4], [5, 4], [5, 4]]
  or [[5, 4], [5, 4], [5, 4], [5, 4], [5, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5]]

In:  [[2, 3], [1, 1], [5, 5], [7, 1]]
Out: [[5, 5], [2, 3], [7, 1], [1, 1]]
 or  [[5, 5], [2, 3], [1, 1], [7, 1]]
 or  [[7, 1], [5, 5], [2, 3], [1, 1]]
 or  [[7, 1], [1, 1], [5, 5], [2, 3]]

我是手工制作的,所以如果发现任何错误,请告诉我。


3
您确定可以在多项式时间内解决具有最小突出数的解决方案吗?
COTO

@COTO我相当有信心,是的。
马丁·恩德

嗯 我通常用贪婪算法来解决它,但是我可以轻松地为任何我能想到的“贪婪”标准(导致面积,最大化一维,最大化最小维等)采购导致次优输出的输入。我能想到的唯一其他方法是将书分成多个小组,所有这些小组都具有指数级的最坏情况下的复杂性。我会很感兴趣的看到什么答案。您可能还需要作为规格的一部分,要求简短证明排序的最佳性。
COTO

@COTO我添加了一个有关此的段落,以防万一我真的错了,但是不要指望它。;)
Martin Ender 2014年

以防万一,应该允许不存在多项式时间算法的潜在证据来假设P不等于NP。
xnor 2014年

Answers:


2

珀斯 30

FN_SQFbYIgeeYeb~b]NB)E~Y]]N;sY

这是grc出色算法的直接体现。这是上述pyth程序在其已编译python代码中的精确等效项。

Q = eval(input())
Y = []
for N in sorted(Q)[::-1]:
     for b in Y:
         if Y[-1][-1] >= b[-1]:
             b += [N]
             break
     else:
         Y += [[N]]
print(Psum(Y))

在这种情况下,该Psum(Y)函数等效于python sum(Y,[])

实际的编译和运行代码(来自pyth -d):

Y=[]
Q=copy(eval(input()))
for N in neg(Psorted(Q)):
 for b in Y:
  if gte(end(end(Y)),end(b)):
   b+=[N]
   break
 else:
  Y+=[[N]]
Pprint("\n",Psum(Y))

1
Python翻译需要“ Y = []”,如果您使用的是Python 2,请删除eval,并且总和需要第二个参数sum(Y,[])。所有这些都应该在Pyth中起作用,只是翻译不会自动包括在内。
xnor 2014年

@xnor最后一行实际上是:Pprint("\n",Psum(Y))。我认为他可能是为了方便起见简化了它,连同所有-1s等Psum实际上运行起来更像reduce(lambda x,y:x+y, Y[1:], Y[0])
FryAmTheEggman 2014年

20

Python,113

P=[]
for n in sorted(input())[::-1]:
 for p in P:
  if p[-1][1]>=n[1]:p+=[n];break
 else:P+=[[n]]
print sum(P,[])

在按降序对书籍列表进行排序后(先按宽度,然后按高度),这会将书籍分成无重叠的一堆。为了确定每本书的放置位置,将其高度与每堆中第一本书的高度进行比较。将其放置在可能的第一个桩上,否则将创建一个新桩。

我对时间的复杂性不是很好,但是我相信它会出现ON 2)的最坏情况。有两个循环,每个循环最多N次迭代。我还使用了Python的内置排序,即On log n)。


我关于该算法产生最佳解决方案的第一个证据确实是不正确的。非常感谢@xnor和@ Sp3000在聊天中进行了精彩的讨论,以证明这一点(您可以从此处开始阅读)。在计算出正确的证明后,@ xnor发现它的一部分已经完成(Dilworth定理)。

无论如何,这是证明的概述(贷记@xnor和@ Sp3000)。

首先,我们定义反堆或反链的概念(引自@xnor):

反堆是一系列高度递减的书,但宽度增加,
因此,每本连续书的高度都严格较高,但宽度却严格较小。
请注意,反堆中的任何书都悬在反堆中的任何其他书上,
因此,反堆中没有两本书可以在同一堆
因此,如果你能找到的X书籍antipile,那么那些书必须在不同的桩
所以,最大的antipile的大小是一个较低的桩数绑定

然后,我们按书籍的宽度(第一)和高度(第二)*降序对它们进行排序。

对于每本书B,我们执行以下操作:

  1. 如果B可以放在第一堆上,我们将其放置在那里并继续前进。
  2. 否则,我们会找到最早的*堆xB可以放在上面。如有必要,这可以是新的堆。
  3. 接下来,我们将B链接到P,其中P是上一堆x-1上的第一本书。
  4. 我们现在知道:
    • B的宽度严格*小于P,因为书是按宽度降序排列的
    • B的高度严格大于P,否则我们将B放置在P的顶部

现在,我们已经建立了从每本书(第一堆中的书除外)到上一堆中宽度更大,高度更低的书的链接。

@ Sp3000的出色图表很好地说明了这一点:

通过遵循从最后一堆(右侧)到第一堆(左侧)的任何路径,我们得到了一个反桩。重要的是,该反堆的长度等于堆数。因此,所使用的桩的数量是最小的。

最后,由于我们将书籍整理成最少数量的无重叠书堆,因此我们可以将它们堆叠在一起,以获得一堆最少重叠的书堆。

* 此有用的评论解释了几件事


3
+1为解释性证明,并链接到讨论。对xnor等的支持。
COTO 2014年

我需要澄清的是,迪尔沃思定理并不能涵盖全部证明,而仅涉及最小数量的桩等于最大尺寸的反桩这一事实。
xnor 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.