计算最大栅栏布置


9

背景

我要建篱笆。为此,我收集了一堆电线杆,并将其固定在地面上。我还收集了很多木板,将它们钉在电线杆上,以制作实际的围栏。我在建造东西时往往会被带走,很可能我会一直把木板钉在电线杆上,直到没有更多的地方放它们了。我想让你列举出我最终可能会遇到的障碍。

输入项

您的输入是一个二维整数坐标列表,以任何方便的格式表示极点的位置。您可以假定它不包含任何重复项,但是不能假设有关其顺序的任何信息。

这些板由两极之间的直线表示,为简单起见,我们仅考虑水平和垂直板。如果两极之间没有其他极或板,则可以将它们连接在一起,这意味着两板不能互相交叉。如果不能在其中添加新的板,则极和板的布置是最大的(等效地,在任何两个水平或垂直对齐的极之间有一个极或一个板)。

输出量

您的输出是可以使用极点构造的最大排列数。

考虑输入清单

[(3,0),(1,1),(0,2),(-1,1),(-2,0),(-1,-1),(0,-2),(1,-1)]

从顶部看,杆的相应排列如下所示:

  o
 o o
o    o
 o o
  o

可以使用这些极点构造三种最大的排列方式:

  o        o        o
 o-o      o|o      o-o
o----o   o||| o   o| | o
 o-o      o|o      o-o
  o        o        o

因此正确的输出是3

规则

您可以编写函数或完整程序。最低字节数获胜,并且不允许出现标准漏洞。

测试用例

[] -> 1
[(0,0),(1,1),(2,2)] -> 1
[(0,0),(1,0),(2,0)] -> 1
[(0,0),(0,1),(1,0),(1,1)] -> 1
[(1,0),(0,1),(-1,0),(0,-1)] -> 2
[(3,0),(1,1),(0,2),(-1,1),(-2,0),(-1,-1),(0,-2),(1,-1)] -> 3
[(0,0),(4,0),(1,1),(1,-2),(3,1),(3,-2),(2,-1),(4,-1)] -> 3
[(0,0),(4,0),(1,1),(1,-2),(3,1),(3,-2),(2,-1),(4,-1),(0,-1)] -> 4
[(0,0),(4,0),(1,1),(1,-2),(3,1),(3,-2),(2,-1),(0,-1),(2,2)] -> 5
[(0,0),(4,0),(1,1),(1,-2),(3,1),(3,-2),(2,-1),(4,-1),(0,-1),(2,2)] -> 8

1
该示例似乎有两次(-2,0)。其中之一应该是(2,0)吗?
isaacg 2015年

@isaacg其实应该(0,-2)不错。现在改变。
Zgarb 2015年

Answers:


5

Mathematica,301个字节

(t~SetAttributes~Orderless;u=Subsets;c=Complement;l=Select;f=FreeQ;Count[s=List@@@l[t@@@u[Sort@l[Sort/@#~u~{2},!f[#-#2&@@#,0]&]//.{a___,{x_,y_},{x_,z_},b___,{y_,z_},c___}:>{a,{x,y},b,{y,z},c}],f[#,t[{{a_,b_},{a_,c_}},{{d_,e_},{f_,e_}},___]/;d<a<f&&b<e<c]&],l_/;f[s,k_List/;k~c~l!={}&&l~c~k=={},{1}]])&

这是一个未命名的函数,它将坐标作为嵌套List并返回整数。也就是说,您可以为其命名并调用它,也可以追加

@ {{3, 0}, {1, 1}, {0, 2}, {-1, 1}, {-2, 0}, {-1, -1}, {0, -2}, {1, -1}}

缩进:

(
  t~SetAttributes~Orderless;
  u = Subsets;
  c = Complement;
  l = Select;
  f = FreeQ;
  Count[
    s = List @@@ l[
      t @@@ u[
        Sort @ l[
          Sort /@ #~u~{2}, 
          !f[# - #2 & @@ #, 0] &
        ] //. {a___, {x_, y_}, {x_, z_}, b___, {y_, z_}, c___} :> 
              {a, {x, y}, b, {y, z}, c}
      ],
      f[
        #,
        t[{{a_, b_}, {a_, c_}}, {{d_, e_}, {f_, e_}}, ___] 
          /; d < a < f && b < e < c
      ] &
    ], 
    l_ /; f[
      s, 
      k_List /; k~c~l != {} && l~c~k == {}, 
      {1}
    ]
  ]
) &

我什至无法开始表达这种实现方式多么幼稚...它绝对不能再蛮力了...

  • 获取所有(无序)极点。
  • 将每对和所有对按标准顺序排序。
  • 丢弃不共享一个坐标的对(即,不能通过正交线连接的对)。
  • 丢弃对可以由两个较短的对组成(这样就o--o--o只能产生两个栅栏,而不是三个)。
  • 获取这些对的所有子集-即栅栏的所有可能组合。
  • 滤除栅栏彼此交叉的组合。
  • 计算在列表中找不到严格的超集的所得围栏集的数量。

令人惊讶的是,它确实可以立即解决所有测试用例。

我发现的一个很巧妙的技巧是Orderless减少了我必须匹配的模式数量。本质上,当我想将带有交叉栅栏的栅栏集沟放开时,我需要找到一对垂直和水平栅栏并检查其状况。但是我不知道它们将以什么顺序出现。由于列表模式通常与顺序相关,因此将导致两个非常长的模式。因此,我改为使用t带有t @@@- 的函数替换周围的列表,该函数未定义,因此按原样保留。但是该函数是Orderless,因此我可以检查模式中的单个顺序,Mathematica会针对所有排列检查它。之后,我使用放回列表List @@@

我希望有一个内置的a)Orderless,b)没有 Listable和c)没有为0参数或列表参数定义。那我可以代替t它。但是似乎没有这样的运算符。


当您考虑Mathematica是否做得正确或足够快时,答案是“是”。
seequ

好吧,这和我的参考实现一样幼稚。:P
Zgarb 2015年

1

Haskell,318个字节

import Data.List
s=subsequences
k[(_,a,b),(_,c,d)]|a==c=f(\w->(1,a,w))b d|1<2=f(\w->(2,w,b))a c
f t u v=[t x|x<-[min u v+1..max u v-1]]
q l=nub[x|x<-map(k=<<)$s[a|a@[(_,n,m),(_,o,p)]<-s l,n==o||m==p],x++l==nubBy(\(_,a,b)(_,c,d)->a==c&&b==d)(x++l)]
m=q.map(\(a,b)->(0,a,b))
p l=sum[1|x<-m l,all(\y->y==x||x\\y/=[])$m l]

用法:p [(1,0),(0,1),(-1,0),(0,-1)]。输出:2

怎么运行的:

  • 创建输入列表的所有子列表,并使它们具有两个元素以及相等的x或y坐标。这是可以在其中建立围栏的所有两极的列表。
  • 创建它的所有子列表
  • 为每个列表添加板
  • 删除xy坐标出现两次(板和极)的列表
  • 由于直接相邻的极点(例如(1,0)和(1,1)),因此删除重复列表(仅板)来处理多个空列表
  • 保留那些不是另一个列表的严格子列表的列表
  • 计算剩余列表
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.