我需要一种能够解析2D数组并返回最大连续矩形的算法。作为参考,请看我演示我的问题的图像。
答案 0 :(得分:10)
通常,您使用所谓的扫描线算法来解决这些问题。他们在您的案例候选矩形中一次检查一行(或扫描线)的数据,从而构建您正在寻找的答案。
这里概述了它如何运作。
从0..6对图像中的所有行进行编号,我将自下而上进行操作。
检查第0行你有两个矩形的开头(我假设你只对黑色方块感兴趣)。我将使用(x,y,width,height)来指代矩形。两个活动矩形是(1,0,2,1)和(4,0,6,1)。您将这些添加到活动矩形列表中。此列表按增加x坐标排序。
现在您已完成扫描线0,因此您可以增加扫描线。
检查第1行,您可以查看是否有以下任何行:
当您沿着该行工作时,您将看到您有一个新的活动矩形(0,1,8,1),我们可以将现有的一个活动矩阵增长到(1,0,2,2)并且我们需要删除活动(4,0,6,1)用两个较窄的替换它。我们需要记住这个。这是我们迄今为止看到的最大规模。它被两个新的活动替换:(4,0,4,2)和(9,0,1,2)
因此,在发送扫描线1时,我们有:
以这种方式继续,直到扫描线用完为止。
棘手的部分是编写沿扫描线运行的例程,更新活动列表。如果你做得正确,你只会考虑每个像素一次。
希望这会有所帮助。描述它有点棘手。
答案 1 :(得分:5)
我喜欢这种区域增长方法。
......将是一个彻底的,但也许不是最有效的方式。
我想你需要回答一个哲学问题“点线是一个瘦的矩形吗?”如果一行==一个细长矩形,您可以进一步优化:
使用第一种方法检查您的工作。我认为Knuth说“......过早的优化是万恶之源。”
HTH,
佩里
ADDENDUM:稍后进行了几次编辑,我认为这个答案值得小组投票。
答案 2 :(得分:2)
一个直接的方法是循环遍历网格中的所有潜在矩形,找出它们的区域,如果它大于当前最高区域,则选择它作为最高区域:
var biggestFound
for each potential rectangle:
if area(this potential rectangle) > area(biggestFound)
biggestFound = this potential rectangle
然后你只需要找到潜在的矩形。
for each square in grid:
recursive loop 1:
if not occupied:
grow right until occupied, and return a rectangle
grow down one and recurse (call loop 1)
这将复制很多工作(例如,你将重新评估很多子矩形),但它应该给你一个答案。
修改强>
另一种方法可能是从网格大小的单个正方形开始,然后“减去”占用的正方形,最后得到一组最终的潜在矩形。这里可能有使用quadtrees的优化机会,并确保您按顺序“按顺序”保持拆分矩形,从上到下,从左到右,以防您需要在算法中进一步重新组合矩形。 / p>
如果您实际上是从矩形数据(对于“填充网格”集)开始,而不是松散的像素网格,那么您可以轻松地从矩形/区域减法算法中获得更好的效果。
我不打算为此发布伪代码,因为这个想法完全是实验性的,我不知道对于松散的像素网格,perf是否会更好;)
Windows系统“区域”和“脏矩形”以及一般的“时间缓存”可能是更好的灵感来提高效率。如果这是一个图形算法,还有很多z缓冲技巧......
答案 3 :(得分:1)
使用动态编程方法。考虑函数S(x,y)使得S(x,y)保持最大矩形的区域,其中(x,y)是矩形的最右下角单元; x是行坐标,y是矩形的列坐标。
例如,在你的图中,S(1,1)= 1,S(1,2)= 2,S(2,1)= 2,S(2,2)= 4.但是,S (3,1)= 0,因为此单元格已填满。 S(8,5)= 40,表示最右边单元格为(8,5)的最大矩形区域为40,这恰好是本例中的最优解。
您可以从S(x-1,y),S(x,y-1)和S(x-1,y-1)的值轻松编写S(x,y)的动态编程方程。使用它可以在O(mn)时间内获得所有S(x,y)的值,其中m和n是给定表的行和列维度。一旦知道所有1< = x< = m的S(x,y),并且对于所有1< = y< = n,我们只需找到x和y,其中S(x, y)是最大的;此步骤也需要O(mn)时间。通过保留添加数据,您还可以找到最大矩形的边长。
整体复杂度为O(mn)。要了解更多相关信息,请阅读第15章或Cormen的算法手册,特别是第15.4节。