找到两个覆盖所有点的最小区域的矩形

时间:2017-11-17 11:45:21

标签: algorithm geometry computational-geometry

你得到一个n点,在数组中未分类。您应该找到两个覆盖所有点的矩形,它们不应重叠。矩形的边缘应平行于x或y纵坐标。
该程序应返回所有这些点所覆盖的最小区域。第一个矩形的区域+第二个矩形的区域。
Here's the first example
我试图解决这个问题。我按X纵坐标对点进行排序,第一个是第一个矩形的最左边一个。当我们通过积分时,我们找到最高点和最低点。我在想,当两个点之间的差异乘以x时,这意味着第一个点是第一个矩形中最右边的一个,第二个点是第二个矩形中最左边的一个。
它应该在第一个示例中给出点时起作用,但是,如果示例是第二个示例则它不起作用。因为它会返回类似这样的东西,这是错误的:The wrong answer for my algorithm

这应该是正确的:

Here's the second example  
然后我想要做两次排序,只是,第二次用Y纵坐标做,然后比较两个总面积。当点按x排序并且点按y排序且较小区域是正确答案时的区域。

2 个答案:

答案 0 :(得分:2)

两个矩形不能重叠,因此一个必须完全在右边或在另一个上面。你想按x值对点进行排序并找到最大差距的想法是好的,但你应该按照你的建议对两个方向进行分类。那会在你的例子中找到正确的矩形。

然而,最大的差距不一定是理想的分裂点。取决于垂直方向上的边界框的范围,分割可以在其他地方。考虑一个带有四个象限的矩形区域,其中两个对角相对的象限填充有点:

在这里,理想的分裂不是最大的差距。

counterexample

您可以通过考虑具有相邻x坐标和y坐标的点之间的所有可能分割来找到理想位置。

  • 按x坐标对点进行排序。
  • 按升序扫描已排序的数组。通过存储最小和最大y坐标来跟踪当前点左侧的最小矩形。为每个点存储这些运行的顶部和底部边框。
  • 现在按降序执行相同的操作,您可以在其中为右侧矩形运行顶部和底部边框。
  • 最后,再次遍历这些点并计算左右最小矩形的区域,以便在两个相邻节点之间进行分割。跟踪最小面积总和。

然后对最小的顶部和底部矩形执行相同的操作。最后两个步骤可以组合在一起,这将为正确矩形的最小边界保存数组。

这应该是O(n·log n)及时:排序是O(n·log n),单个遍是O(n)。对于第一个矩形的运行边界框,你需要O(n)额外的内存。

答案 1 :(得分:1)

第一个观察是矩形的任何边都必须触及其中一个点。没有触及某个点的边缘可能会被拉回,导致面积减少。

给定n个点,因此对于left1,right1,bottom1,top1,left2,right2,bottom2和top2总共有n个选择。这给出了一个简单的O(n ^ 8)算法:尝试所有可能的分配,并记住给出最小总面积(right1 - left1)(top1 - bottom1)+(right2 - left2)(top2 - bottom2)的算法。实际上,你可以跳过任何正确的组合<左或上<底部。这给出了加速,但它不会改变O(n ^ 8)的界限。

另一个观察结果是边缘应该保持在所有点的最小和最大X和Y范围内。找出任何点的最小和最大X和Y值。将这些minX,maxX,minY和maxY称为。至少有一个矩形需要分别在这些级别上有左,右,下和上边缘。

因为必须将minx,maxX,minY和maxY分配给两个矩形中的一个,并且正好有2 ^ 4 = 16种方法,所以您可以尝试四种可能的分配中的每一种,其余的坐标分配为以上。这给出了一个O(n ^ 4)算法:O(n)得到minX,maxX,minY和maxY,然后是O(n ^ 4)来填充minX,maxX的16个赋值中的每一个的四个未赋值变量, minY和maxY到八个边缘坐标。

到目前为止,我们忽略了矩形不重叠的要求。为了适应这种情况,我们必须确保满足以下四个条件中的至少一个:

  1. 在Y坐标h处的水平线,其中top1 <= h <= bottom2
  2. Y坐标h的水平线,top2 <= h <= bottom1
  3. X坐标w处的垂直线,其中right1&lt; = h&lt; = left2
  4. X坐标w处的垂直线,其中右2&lt; = h&lt; = left1
  5. 当且仅当所有这四个条件同时为假时,两个矩形重叠。这允许我们跳过候选解,提供加速但不改变渐近约束O(n ^ 4)。请注意,我们需要特别检查这个条件,否则,最佳解决方案可能会重叠(练习:显示这样的示例)。

    让我们试着再多花些时间。假设我们通过上面的条件#1有非重叠的矩形。那么h有n个选择;我们可以尝试这n个选项中的每一个,然后通过找到结果中的点的最小和最大坐标来确定结果选择的区域。通过尝试h的所有n个选择,我们可以确定最佳情况&#34;垂直分裂。我们不需要尝试条件#2,因为唯一的区别在于矩形的排序是任意的。我们还必须尝试使用​​水平分割的条件#3。这表明O(n ^ 2)算法:

    1. 对于每个点,请选择h = point.y
    2. 使用point.y&lt; = h和point.y&gt;将点分成组。小时。
    3. 找到两个点子集的最小和最大X和Y坐标。
    4. 计算两个矩形区域的总和。
    5. 请记住从上面获得的最小区域和相应的h。
    6. 重复,但使用w和X坐标。
    7. 确定是否为垂直或水平分割获得最小区域
    8. 返回相应的矩形作为答案
    9. 我们能做得更好吗?这是O(n ^ 2)而不是O(n),因为对于hw的每个选择,我们需要找到每个子组的最小和最大坐标。假设通过两个子组进行线性扫描。当水平/垂直扫描时,我们实际上不需要为最小和最大X / Y坐标执行此操作,因为这些将是已知的。我们需要的是解决这个问题的方法:

        

      给定n个点和值h,找到Y坐标不大于h的任何点的最大X坐标。

      我上面给出的一个显而易见的解决方案是O(n ^ 2),但你可以通过巧妙的排序应用找到一个O(n log n)解决方案,甚至可以通过更多的O(n)解决方案来找到聪明的方法。我不会尝试这个。

      我们的解决方案是O(n ^ 2);理论上最优的解是Omega(n),因为你必须至少看看所有的点。所以我们非常接近,但还有改进的余地。