假设我有一个块网格,7x12。我们使用颜色'*','%','@'和一个空单元' - '。
1 2 3 4 5 6 7
- - - - - - - 1
- - - - - - - 2
% % - - - - - 3
% % - - - - * 4
% % - - - @ % 5
@ @ @ - - @ % 6
@ @ * * * - * 7
* * * % % % % 8
% @ @ % * * % 9
% @ % % % % % 10
* * * % % @ @ 11
* * @ @ @ @ * 12
我想在这个特定最小尺寸的网格中找到矩形,并且我可以找到最大的矩形然后更小,直到找不到大于或等于最小尺寸的矩形。
在此示例中,请考虑最小大小1x4,4x1,2x2,因此1x3无效但2x3无效。如果我们想要最大的矩形,我们会发现以下内容:
请注意,矩形不能在每个其他空间中,它们不能重叠。例如,未提及(4,10)处的2x2矩形,因为它将与(3,10)处的5x1矩形重叠。
所有都是完全有效的矩形:它们等于或大于最小尺寸,每个矩形的所有块都是相同的颜色。
我想要的是以编程方式执行此操作。当你告诉别人在网格中找到矩形时,他会立即找到它们,而不考虑它。问题是,我怎样才能编写一个同样的算法?
我考虑过强制执行,但我需要算法尽可能快地执行,因为它需要在有限(移动)设备上的非常小的时间范围内执行很多。
我在互联网上看到很多关于矩形的问题,但我很惊讶这个问题还没有被问过。我觉得太难了,或者没有人想做过这样的事情?
答案 0 :(得分:9)
分别调用输入数组W和H的宽度和高度。
这种方法是“贪婪的”,因为如果有多种方法可以将纯色区域划分为最大矩形,则无法保证选择最大的矩形序列。 (例如,可能有几个矩形的左上角位于(10,10),其面积为16:16x1,8x2,4x4,2x8,1x16。在这种情况下,一个选项可能会产生更大的矩形“下游“但我的算法并不保证做出这样的选择。”如果有必要,你可以使用回溯找到这个整体最佳矩形系列,但我怀疑在最坏的情况下这可能会非常慢。
我提到的最大矩形算法是针对单色矩形设计的,但是如果你不能使它适应你的多色问题,你可以在开始第2步之前为每种颜色运行一次。
答案 1 :(得分:0)
注意:这是在你试图找到最大的k
矩形的假设下运作的。
我们知道,在最坏的情况下,我们必须至少查看一次网格中的每个节点。这意味着我们最好的最坏情况是O(len*wid)
。
你的蛮力将是O(len*len*wid*wid)
,其天真的方法是“检查某个点上的矩形是O(len*wid)
,然后你O(len*wid)
次这样做。
可能您发现情况并非如此,因为每次找到矩形时,您都有可能减少问题空间。我觉得“检查每个矩形”的蛮力方法将是最好的方法。但是,有些事情可以加快速度。
基本算法:
for(x = 1 .. wid) {
for(y = 1 .. len) {
Rectangle rect = biggestRectOriginatingAt(x,y);
// process this rectangle for being added
}
}
跟踪最大的k
矩形。随着您的进展,您可以搜索符合条件的矩形可能位于的周长。
Rectangle biggestRectOriginatingAt(x,y) {
area = areaOf(smallestEligibleRectangle); // if we want the biggest k rect's, this
// returns the area of the kth biggest
// known rectangle thus far
for(i = 1 .. area) {
tempwid = i
templen = area / i
tempx = x + tempwid
tempy = y + templen
checkForRectangle(x,y,tempx,tempy); // does x,y --> tempx,tempy form a rectangle?
}
}
这可以让你在大型搜索结束时获得巨大的性能提升(如果这是一个小搜索,你没有获得那么多,但你不关心因为它是一个小搜索!)
这对于更随机的分配也不起作用。
O(len*wid)
,这是一个很小的成本。这将允许您搜索大矩形的最可能区域。请注意,这些方法都不能减少最坏的情况。但是,它们确实减少了实际预期的运行时间。
答案 2 :(得分:0)
我必须为我的第一人称射手解决一个非常类似的问题。我在输入中使用它:
[] [] [] [] [] [] [] []
[] [] [] [X] [] [] [] []
[] [X] [X] [X] [X] [X] [X] [X]
[] [] [X] [X] [X] [X] [] []
[] [X] [X] [X] [X] [] [] []
[] [X] [X] [X] [X] [] [] []
[] [] [X] [] [] [] [] []
[] [] [] [] [] [] [] []
我在输出中得到了:
[] [] [] [] [] [] [] []
[] [] [] [A] [] [] [] []
[] [B] [G] [G] [G] [F] [E] [E]
[] [] [G] [G] [G] [F] [] []
[] [d] [G] [G] [G] [] [] []
[] [d] [G] [G] [G] [] [] []
[] [] [C] [] [] [] [] []
[] [] [] [] [] [] [] []
This schema更好。源代码(在GNU通用公共许可证版本2下)是here,它被大量评论。您可能需要根据需要对其进行调整,如j_random_hacker建议的那样。
答案 3 :(得分:0)
我自己的解决方案是找到最大的矩形,使用与@j_random_hacker答案相同的algorithm,然后将剩余区域分成4个区域,并在每个区域中再次递归搜索最大的矩形。
它会找到比接受答案更少的矩形,因为我发现在搜索最大的矩形时,很难采用该算法来保存每个中间矩形。 该算法会跳过所有较小的矩形,因此我们必须遍历网格中的每个点,以保存每个可能的矩形,然后丢弃较小的矩形,这会使算法恢复到O(M³·N³)复杂度。
我们可以用两种方式拆分剩余区域,算法将检查两者,并将使用覆盖大部分区域的选项,因此它将执行两次递归调用 - 第一次计算区域,第二次填充输出数组。
****|***|*** ************
****|***|*** ************
****#####*** ----#####---
****#####*** vs ****#####***
****#####*** ----#####---
****|***|*** ************
我们只能留下一个区域分割选择,以使算法运行得更快,因为这个区域比较并没有对检测到的矩形数量做出太大改进,说实话。
编辑:我刚刚意识到递归检查两种分裂变体会将算法提升到阶乘复杂度,就像O(min(M,N)!)。所以我禁用了第二个区域分割,这使算法的复杂度大约为O(M⋅N·log(M⋅N))。