让我首先阐述这个问题,因为我认为它的意思是:
要找到的矩形具有水平(常数 y)和垂直(常数 x)边界,即没有“旋转”。
矩形在其每条边的“内部”(“开放”)部分上至少有一个点,即不在拐角处。(它的任何边上可能有多个点,并且 ALSO 点在它的角上。)这排除了“无限”解决方案,因为所有点都有有限的 x,y。它还排除了我们可能打算仅通过 TopLeft 和 BottomRight 点以及类似结构来定义矩形的情况。
我们在满足上述条件的所有矩形中寻找面积最大的矩形。
假设上述是问题的正确(重新)表述,我相信它是一个二维优化问题,可能有许多“局部”最优值。任何“从某事开始并逐渐改进”类型的方法都只会找到局部最优值,而不一定是全局最优值。
我无法想出比 O(N^2) 方法更好的方法,包括 - 粗略地说 - N 次局部优化,其中每个局部优化都是 O(N)。我将使用一些代码片段(部分是伪代码或备注)来勾勒该方法,以用于本地优化的基本部分。其余部分“基本相同”,填写起来应该不难。
为了减少措辞而不会变得不准确,从今以后,我将指的是“(矩形的)边缘上的一个点”,一个位于边缘“内部”部分的点,而不是角落。同样,“矩形”是指一个“合格的”反应角,即满足基本条件的反应角:内部没有点,每个边缘上至少有一个点。
现在,LOCAL 优化由点集中的特定点“定义”,结合来自 {Left, Top, Right, Bottom} 的特定“边界类型”。假设我们选择了一个点 L 和边框类型“Left”;局部最优解定义为左边缘有 L 的“最佳”(最大面积)矩形。
我们将构建所有 (L,Left)-矩形并跟踪我们在途中找到的最大的一个。
观察:任何在右边界上具有点 R 的 (L,Left) 矩形,其上边界上必须有一个点 T,其下边界上必须有一个点 B,其中
L.x < T.x < R.x
L.x < B.X < R.X
B.y < L.y < T.y
B.y < R.y < T.Y
现在想象一下,我们以 x 顺序扫描点,从 L 开始。
一方面:在我们找到 R 之前,上述条件表明我们必须首先遇到 T 和 B。
另一方面,一旦我们从中间遇到的所有 y > Ly 的点中找到 R,我们现在将被具有最低 y 的点所包围。同样适用于底部边框。
N 为点数 {P},令 index_X[] 为 x 排序点的索引数组,使得 P[index_X[i]]x <= P[index_X[j]].x 每当 i 为小于 j。
此外,令 iL 为 L 在 x 排序数组中的索引:P[index_X[iL]] = L。
在下面的代码片段中(VB 语法;翻译成任何语言都不会太难),我们首先确定“some”T(顶部边缘点)和“some”B(底部边缘点)边缘点)。然后,我们继续扫描,每当我们找到一个完成矩形的点 R,我们: (a) 计算面积以更新最优值,如果它更大;(b) 用找到的 R 替换 T 或 B(取决于 R 是高于 L 还是低于 L),然后重复搜索 R。
Dim indx As Integer = iL + 1 'first point to consider on the right of L
Dim T As PointF = Nothing
Dim B As PointF = Nothing
' find T,B
While (indx < N AndAlso (T = Nothing OrElse B = Nothing))
Dim Q As PointF = Pts(indx_X(indx))
If (Q.Y > L.Y) Then
' a candidate for T
If (T = Nothing) OrElse (Q.Y < T.Y) Then
T = Q
End If
ElseIf (Q.Y < L.Y) Then
' a candidate for B
If (B = Nothing) OrElse (Q.Y > B.Y) Then
B = Q
End If
Else
Return -1 'Failure for L altogether: Q has exactly the same y-value as L and we didn't find yet both T and B.
End If
indx += 1
End While
If (T = Nothing OrElse B = Nothing) Then
Return -1 'Failure for L: we have exhausted all points on the right without finding both T and B.
End If
' we have "some" T and B, now proceed to find all (L,Left) rectangles
' intialize result (= max area from (L,Left)-rectangles).
Dim result As Double = -1
' the next point to consider
indx += 1
' find successive rectangles, by searching for R, given L (fixed) and T,B (occasionally updated).
While (indx < N)
Dim R As PointF = Pts(indx_X(indx)) 'candidate
If (R.Y = L.Y) Then
' rectangle found, process it.
Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
If area > result Then
result = area
End If
' it all stops here: no further rectangles {L,Left) are possible as they would have this R inside.
Exit While
End If
If (R.Y > L.Y) Then
' a point with y > L.Y:
' if it is above T we can ignore it.
' if is below T, it is the R-bound for the rectangle bounded by L,T,B
If (R.Y < T.Y) Then
' process the rectangle
Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
If area > result Then
result = area
End If
' move on, understanding that for any NEXT rectangle this R must be the upperbound.
T = R
End If
Else 'R.Y < L.Y
' a point with y < L.Y
' if it is below B we can ignore it.
' if it is above B, it is the R-bound for the rectangle bounded by L,T,B
If (R.Y > B.Y) Then
' process the rectangle
Dim area As Double = (R.X - L.X) * (T.Y - B.Y)
If area > result Then
result = area
End If
' move on, understanding that for any NEXT rectangle this R must be the lowerbound.
B = R
End If
End If
indx += 1
End While
Return result
整体的解决方案当然是:对每个点P,在opt(P,Left), opt(P,Right), opt(P,Top), opt(P,Bottom)中找到最优的,然后求最大值在所有 P 中。Right、Top、Bottom 的变体当然与我上面勾勒的 opt(Left) 非常相似。预排序(获取 x 顺序和 y 顺序的索引(用于处理 (P,Top) 和 (P,Bottom) 情况)是 O(nLogn),局部优化是 O(n) - 查看代码,所以总体复杂度为 O(n^2)。
补充说明 - 因为最初的公式对我来说不是很清楚:如果矩形也可以由 CORNER 点限定,那么上面需要一些小的调整(主要是在不等式条件中添加等号),但它不会改变本质上是算法。