第一个观察结果是,矩形的任何一条边都必须接触其中一个点。没有接触点的边可以向后拉,从而减少面积。
给定n个点,因此共有n种选择左1、右1、底部1、顶部1、左2、右2、底部2和顶部2。这已经提供了一个简单的O(n^8)算法:尝试所有可能的分配,并记住给出最小总面积(right1-left1)(top1-bottom1)+(right2-left2)(top2-bottom2)的那个分配方案。确实,您可以跳过任何right<left或top<bottom的组合。这样可以加速,但不会改变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,然后对于将minX、maxX、minY和maxY分配给8个边界坐标的16个分配中的每一个,需要O(n^4)时间来填充四个未分配变量。
到目前为止,我们忽略了矩形不重叠的要求。为了容纳这一点,我们必须确保以下至少一种情况成立:
- 在Y坐标h上的水平线段,其中top1 <= h <= bottom2
- 在Y坐标h上的水平线段,其中top2 <= h <= bottom1
- 在X坐标w上的垂直线段,其中right1 <= h <= left2
- 在X坐标w上的垂直线段,其中right2 <= h <= left1
如果同时满足以下四个条件,则两个矩形重叠,否则不重叠。这使我们能够跳过候选解,从而加快速度,但不改变渐近界限O(n^4)。请注意,我们需要特别检查此条件,否则最优解可能会有重叠(练习:展示这样的例子)。
让我们试着进一步缩短时间。根据上述第1个条件,假设我们有非重叠矩形。然后可以选择n个h值;我们可以尝试每个h值,然后通过找到结果两半中点的最小和最大坐标来确定所选区域的面积。通过尝试所有n个h值的选择,我们可以确定“最佳情况”垂直分割。我们无需尝试第2个条件,因为唯一的区别在于矩形的顺序是任意的。我们还必须使用水平分割尝试第3个条件。这建议了一个O(n^2)算法:
- 对于每个点,选择h = point.y。
- 将点分成两组,一组是point.y <= h,另一组是point.y > h。
- 找到两个子集中X和Y坐标的最小值和最大值。
- 计算两个矩形的面积之和。
- 记住从上面得到的最小面积和相应的h。
- 重复以上步骤,但使用w和X坐标。
- 确定最小面积是通过垂直还是水平切割获得的。
- 返回相应的矩形作为答案。
我们能做得更好吗?这是O(n^2)而不是O(n),因为对于每个h和w的选择,我们需要扫描每个子组以找到最小和最大坐标。这假设通过两个子组进行线性扫描。事实上,当水平/垂直扫描时,我们不需要为min和max X/Y坐标执行此操作,因为那些将是已知的。我们需要解决的问题是:
给定n个点和一个值h,找到任何Y坐标不大于h的点的最大X坐标。
我提供的明显解决方案是O(n^2),但你可能可以通过巧妙地应用排序找到一个O(n log n)的解决方案,甚至通过一些更聪明的方法找到一个O(n)的解决方案。我不会尝试这样做。
我们的解决方案是O(n^2);理论上最优解是Omega(n),因为您必须至少查看所有点。所以我们已经很接近了,但还有改进的空间。