我想知道不同的算法,找到点缀着障碍物的二维地图中的最大正方形。
一个例子,其中o
将成为障碍:
...........................
....o......................
............o..............
...........................
....o......................
...............o...........
...........................
......o..............o.....
..o.......o................
最大的广场将是(如果我们选择第一个广场):
.....xxxxxxx...............
....oxxxxxxx...............
.....xxxxxxxo..............
.....xxxxxxx...............
....oxxxxxxx...............
.....xxxxxxx...o...........
.....xxxxxxx...............
......o..............o.....
..o.......o................
找到它的最快算法是什么?复杂度最小的那个?
编辑:我知道人们对接受的答案中解释的算法感兴趣,所以我制作了一个文档来解释它,你可以在这里找到它:
https://docs.google.com/document/d/19pHCD433tYsvAor0WObxa2qusAjKdx96kaf3z5I8XT8/edit?usp=sharing
答案 0 :(得分:7)
以下是如何在最佳量时间内执行此操作,O(nm)。这是建立在@dukeling的洞察力之上,您永远不需要检查尺寸小于当前已知最佳解决方案的解决方案。
关键是能够构建一个可以在O(1)时间内回答此查询的数据结构。
要解决这个问题,我们将支持回答一个稍微更难的问题,也在O(1)中。
通过矩形计数问题的答案,很容易回答方形存在问题。
要回答矩形计数问题,请注意,如果您已经预先计算了左上角开始的每个矩形的答案,那么您可以通过某种方式回答从r1,c1到r2,c2的一般问题。仅使用从左上角开始的矩形的聪明/包含排除策略
c1 c2
-----------------------
| | | |
| A | B | |
|_____________|____| | r1
| | | |
| C | D | |
|_____________|____| | r2
|_____________________|
我们想要D里面的东西计数。就我们从左上角预先计算的数量来说。
Count(D) = Count(A ∪ B ∪ C ∪ D) - Count(A ∪ C) - Count(A ∪ B) + Count(A)
你可以通过做一些聪明的行/列部分和来预先计算O(nm)中的所有左上角矩形,但我会把它留给你。
然后回答您想要解决的问题,只需检查可能的解决方案,从至少与您最熟悉的解决方案一样的解决方案开始。你知道的最好只会达到最小(n,m)次总数,所以best_possible增量很少发生,几乎所有的方格都会在O(1)时间内被拒绝。
best_possible = 0
for r in range(n):
for c in range(m):
while True:
# this looks O(min(n, m)), but it's amortized O(1) since best_possible
# rarely increased.
if possible(r, c, best_possible+1):
best_possible += 1
else:
break
答案 1 :(得分:5)
一个想法,利用二分搜索。
基本理念:
从左上角开始。看看1x1方格是否可行。
原生方法:
我们可以简单地检查每个步骤中每个方块的每个可能的单元格,但这是相当低效的。
优化方法:
当增加平方大小时,我们可以在下一行和列上进行二进制搜索,以查看该行/列是否包含任何位置的障碍物。
当向右移动时,我们可以对每个下一列进行二元搜索,以确定该列是否包含任何位置的障碍物。
向下移动时,我们可以在目标位置的每一列上执行类似的二进制文件。
实施说明:
首先,我们需要遍历所有行和列,并设置包含每个行的障碍位置的数组,我们可以将其用于二进制搜索。
运行时间:
我们进行2次二进制搜索以增加方块大小,并且方块大小最大化网格的大小,因此相当小(O(min(m,n) log max(m,n))
)并由下面的主导。
除此之外,对于每个位置,我们对列进行单个二进制搜索。
因此,对于包含m
列和n
行的网格,整体复杂度为O(mn log m)
。
但请注意,当网格稀疏时,我们实际上在下面搜索的内容很少。
示例:强>
对于你的例子:
012345678901234567890123456
0...........................
1....o......................
2............o..............
3...........................
4....o......................
5...............o...........
6...........................
7......o..............o.....
8..o.......o................
我们首先在左上角尝试一个1x1的方格,这样可行。
然后是2x2平方。为此,我们对第1行的范围[0,1]
进行二分搜索,该范围可以简单地用{4}
表示 - 一个与障碍物所在位置对应的单个位置的数组。我们还对第1列的范围[0,1]
进行二分搜索,该范围不包含任何障碍,因此是一个空数组 - {}
。
然后是3x3平方。为此,我们在第2行对[0,2]
进行二分搜索,其中包含位置12处的1个障碍,因此{12}
。我们还对第2列的[0,2]
进行二分搜索,其中包含位置8的障碍,因此{8}
。
然后一个4x4平方。为此,我们在第3行[0,3]
上对{}
进行二分搜索。对于第3列的[0,3]
- {}
。
然后是5x5平方。为此,我们在第4行[0,4]
上对{4}
进行二分搜索。对于[0,4]
第4列 - {1,4}
。
这是我们实际找到的第一个。在[0,4]
范围内,我们在行和列中都找到4
(我们只需要找到其中一个)。所以这表明失败了。
从这里开始,[0,4]
对第4列进行二元搜索(再次 - 不是必需的)。然后找到[0,4]
的二进制搜索列5-8,找不到任何一个,因此从位置5,0
开始的方格是下一个可能的候选者。
所以从这里我们尝试将平方尺寸增加到5x5,然后工作,然后是6x6和7x7,这是有效的。
然后我们尝试8x8,这不起作用。
等等。
我知道二进制搜索,但你的工作方式是什么?
所以我们基本上在一组值中进行范围搜索。这很容易做到。首先搜索范围的起始值,然后搜索结束值。如果我们达到相同的点,则范围内没有值。
我们并不关心范围内存在哪些值,只是有没有。
答案 2 :(得分:2)
所以这是一个粗略的方法。
Store the x-y positions of all the obstacles.
For each obstacle O
find obstacle C that is nearest to it column-wise.
find obstacle R-top that is nearest to it row-wise from the top.
find obstacle R-bottom that is nearest to it row-wise from the bottom.
if (|R-top.y - R-bottom.y| != |O.x - C.x|) continue
Size of the square = Abs((R-top.y - R-bottom.y) * (O.x - C.x))
Keep track of the sizes and positions to find the largest square
复杂度大约是O(k^2)
,其中k
是障碍物的数量。如果使用二分查找,可以将其减少到O(k * log k)
。
答案 3 :(得分:2)
以下SO文章与您尝试解决的问题相同/相似。您可能希望查看这些答案以及对您问题的回答。
这是我使用的基线案例,用简化的Python /伪代码编写。
# obstacleMap is a list of list of MapElements, stored in row-major order
max([find_largest_rect(obstacleMap, element) for row in obstacleMap for element in row])
def find_largest_rect(obstacleMap, upper_left_elem):
size = 0
while not has_obstacles(obstacleMap, upper_left_elem, size+1):
size += 1
return size
def has_obstacles(obstacleMap, upper_left_elem, size):
#determines if there are obstacles on the on outside square layer
#for example, if U is the upper left element and size=3, then has_obstacles checks the elements marked p.
# .....
# ..U.p
# ....p
# ..ppp
periphery_row = obstacleMap[upper_left_elem.row][upper_left_elem.col:upper_left_elem.col+size]
periphery_col = [row[upper_left_elem.col+size] for row in obstacleMap[upper_left_elem.row:upper_left_elem.row+size]
return any(is_obstacle(elem) for elem in periphery_row + periphery_col)
def is_obstacle(elem):
return elem.value == 'o'
class MapElement(object):
def __init__(self, row, col, value):
self.row = row
self.col = col
self.value = value
答案 4 :(得分:0)
这是一种使用递归关系的方法: -
isSquare(R,C1,C2) = noObstacle(R,C1,R,C2) && noObstacle(R,C2,R-(C2-C1),C2) && isSquare(R-1,C1,C2-1)
isSquare(R,C1,C2) = square that has bottom side (R,C1) to (R,C2)
noObstacle(R1,C1,R2,C2) = checks whether there is no obstacle in line segment (R1,C1) to (R2,C2)
Find Max (C2-C1+1) which where isSquare(R,C1,C2) = true
您可以使用动态编程在多项式时间内解决此问题。使用合适的数据结构来搜索障碍物。