3 回答
当然。
要找到一个矩形:
- 从任何空闲单元格开始。假设这将是左上角。
- 看看能不能横向展开。
- 看看是否可以垂直扩展。
- 回溯。
稍后我将对此进行详细说明,但我认为这可以让您快速开始。
我之前的回答是简单的方法。这是一个完全不同的。
算法
识别水平运行并转换为矩形表示法。
currRects
用第 0 行中的矩形初始化列表。将 currRow 设置为 1。
对于 中的每个矩形,
b
在row中currRects
找到与水平线的交点。相交操作定义为a
currRow
i
r(axi-axt,ayi-ayt) i r(bxi-bxt,byi-byt)
它的相关性是两个矩形的
y
范围如何相交。用图形描绘它:bbb aa| | No intersect aa|aa | Partial intersect aa|aaa| a contains b aa|aaa|a a contains b |aa | b contains a |aaa| a equal b |aaa|a a contains b | aa| b contains a | aa|a Partial intersect | |aa No intersect
确定每种情况的必要逻辑基于
ayi
、ayt
、byi
、byt
和 简单,因此这里不再描述。此函数将返回最多 2 个矩形和一个标志的列表。
No intersect ([],true) Partial intersect: ([red,a],true) b contains a: ([red],true) a contains b: ([pres,a],false) a equal b: ([pres],false)
red
并且pres
是将前一个矩形b
与传入的a
. 它可以pres
遍历矩形的宽度(在 y 坐标的意义上),或者red
使用它,但算法上的区别并不重要。第 4 步的核心是保留一个
nextCurrRects
包含 function 返回的所有矩形的列表intersect
。nextCurrRects
当然,最初应该是空的。b
此外,如果对于前面的矩形,currRects
返回的标志是 neverfalse
,则b
需要将其放入最终结果列表中(或者只是打印或“生成”)。使
currRects
等于nextCurrRects
。currRow
如果还没有到达终点,则进行迭代。仍然存在的所有矩形
currRects
也属于结果。
例子
1)inRects= [ r(0-0,2-2), r(1-1,0-2), r(2-2,0-1) ]
2)currRects= [ r(0-0,2-2) ]
3)currRow= 1
4)
nextCurrRects= []
r(1-1,0-2) i r(0-0,2-2) {a contains b} == ( [ r(0-1,2-2), r(1-1,0-2)] ], false )
nextCurrRects == [ r(0-1,2-2), r(1-1,0-2)] ]
5)currRects == [ r(0-1,2-2), r(1-1,0-2)] ]
4)
nextCurrRects= []
r(2-2,0-1) i r(0-1,2-2) {No intersect} == ( [], true )
Never false -> result= [ r(0-1,2-2) ]
r(2-2,0-1) i r(1-1,0-2) {Partial intersect} == ( r(1-2,0-1), true )
nextCurrRects= [ r(1-2,0-1) ]
Never false -> result= [ r(0-1,2-2) , r(1-2,0-1) ]
5)currRects= [ r(1-2,0-1) ]
6)result= [ r(0-1,2-2) , r(1-2,0-1) , r(1-2,0-1) ]
事实上,该算法刚刚“发现”在原始示例中没有考虑第三个结果矩形:
[x][x][3]
[ ][ ][3]
[ ][ ][x]
代码
class Range:
def __init__(self,start,end=None):
self.start= start
self.end= end if end is not None else start
def isEmpty(self):
return self.start > self.end
def isUnit(self):
return self.start == self.end
def intersect(self,other):
return Range( max(self.start,other.start) , min(self.end,other.end) )
def contains(self,other):
return self.start <= other.start and self.end >= other.end
def __repr__(self):
return "Range(%d,%d)" % ( self.start, self.end )
class Rect:
def __init__(self,xRange,yRange):
self.xRange= xRange
self.yRange= yRange
def isEmpty(self):
return self.xRange.isEmpty() or self.yRange.isEmpty()
def isUnit(self):
return self.xRange.isUnit() and self.yRange.isUnit()
def intersect(self,other):
return Range( max(self.start,other.start) , min(self.end,other.end) )
def contains(self,other):
return self.xRange.contains(other.xRange) and self.yRange.contains(other.yRange)
def __repr__(self):
return "Rect(%s,%s)" % ( self.xRange, self.yRange )
def intersect(a,b):
r= Rect( Range(b.xRange.start,a.xRange.end) , a.yRange.intersect(b.yRange) )
brokenB= not a.yRange.contains(b.yRange)
fullyAbsorbedA= b.yRange.contains(a.yRange)
return (r,brokenB,fullyAbsorbedA)
def findOpenRectangles(freeElements,pastRowNum):
# From `freeElements`, compute free runs into `freeRunsPerRow`
from collections import defaultdict
freeRunsPerRow= defaultdict(set)
rowNum= -1
currRun= None
for fe in freeElements :
if fe[0] != rowNum :
if currRun is not None:
freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
currRun= Range(fe[1],fe[1])
rowNum= fe[0]
elif fe[1] == currRun.end + 1 :
currRun= Range(currRun.start,fe[1])
else:
freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
currRun= Range(fe[1],fe[1])
if currRun is not None:
freeRunsPerRow[rowNum]|= { Rect(Range(rowNum),currRun) }
currRun= None
for freeRuns in freeRunsPerRow.items() :
print( freeRuns )
# Yield open rectangles
currRects= set()
for currRow in range(0,pastRowNum) :
continuingRects= set()
startingRects= set( freeRunsPerRow[currRow] )
for b in currRects :
brokenB= True
for a in freeRunsPerRow[currRow] :
modifiedContinuingRect, t_brokenB, t_absorbedA= intersect(a,b)
if not modifiedContinuingRect.isEmpty() and not [ x for x in continuingRects if x.contains(modifiedContinuingRect) ] :
continuingRects-= { x for x in continuingRects if modifiedContinuingRect.contains(x) }
continuingRects|= {modifiedContinuingRect}
if not t_brokenB : brokenB= False
if t_absorbedA : startingRects-= {a}
if brokenB and not b.isUnit() : yield b
currRects= continuingRects
currRects|= startingRects
for b in currRects :
if not b.isUnit() : yield b
测试/示例
使用以下测试代码:
input= []
input.append(" X ")
input.append(" ")
input.append(" X ")
input.append(" X X")
input.append(" ")
input.append(" ")
input.append(" X ")
input.append(" X X ")
input.append(" X X")
# Translates input into a list of coordinates of free elements
freeElements= []
for rowNum, row in enumerate(input):
for colNum, element in enumerate(row):
if element == " " :
freeElements.append( (rowNum,colNum) )
for fe in freeElements :
print( fe )
# Find and print open rectangles
for openRect in findOpenRectangles(freeElements,len(input)) :
print( openRect )
输出如下(一些格式以节省空间和添加的标题):
自由元素
(0, 0), (0, 1), (0, 2), (0, 4), (0, 5), (0, 6), (0, 7)
(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7)
(2, 0), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7)
(3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 6)
(4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7)
(5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7)
(6, 0), (6, 1), (6, 2), (6, 3), (6, 5), (6, 6), (6, 7)
( 7, 0), (7, 1), (7, 2), (7, 4), (7, 5), (7, 7)
(8, 0), (8, 2), (8, 3 ), (8, 4), (8, 5), (8, 6)
freeRunsPerRow
(0, {Rect(Range(0,0),Range(0,2)), Rect(Range(0,0),Range(4,7))})
(1, {Rect(Range(1,1 ) ),Range(0,7))})
(2, {矩形(Range(2,2),Range(2,7)), 矩形(Range(2,2),Range(0,0))})
(3, {Rect(Range(3,3),Range(6,6)), Rect(Range(3,3),Range(0,4))})
(4, {Rect(Range(4,4 ) ),Range(0,7))})
(5, {Rect(Range(5,5),Range(0,7))})
(6, {Rect(Range(6,6),Range(5, 7)), 矩形(范围(6,6),范围(0,3))})
(7, {矩形(范围(7,7),范围(7,7)), 矩形(范围(7,7) ),范围(4,5)), 矩形(范围(7,7),范围(0,2))})
(8, {矩形(范围(8,8),范围(0,0)), 矩形(范围(8,8),范围(2,6))})
findOpenRectangles(freeElements,len(input))
矩形(范围(0,1),范围(0,2))
矩形(范围(1,1),范围(0,7))
矩形(范围(0,2),范围(4,7))
矩形(范围(1,2),范围(2,7))
矩形(范围(3,5),范围(0,4))
矩形(范围(0,5),范围(4,4))
矩形(范围( 4,5),范围(0,7))
矩形(范围(1,5),范围(2,4))
矩形(范围(1,6),范围(2,3))
矩形(范围(4, 6),范围(5,7))
矩形(范围(0,6),范围(6,6))
矩形(范围(3,6),范围(0,3))
矩形(范围(3,7) ,范围(0,2))
矩形(范围(4,7),范围(7,7))
矩形(范围(8,8),范围(2,6))
矩形(范围(7,8),范围(4,5))
矩形(范围(0,8),范围(2,2))
矩形(范围(4,8),范围(5,5))
矩形(范围(0,8),范围(0 ,0))
测试/示例结论
输出正确。所有非平凡的矩形都存在。
可用坐标列表已排序。
所以也有一个全局的,迄今为止最大的矩形,作为列表。
做一个列表遍历,并为每个项目调用一个函数,子列表从项目开始。查找从该项目左上角开始的最大矩形。
public List<Coord> getMaxRectangle(List<Coord> openCoords) {
List<Coord> currentMaxRectangle = null;
for (int i = 0; i < openCoords.size(); ++i) {
// Handle all rectangles starting here:
currentMaxRectangle = maxRectangleStartingAt(i, currentMaxRectangle);
}
return currentMaxRectangle;
}
public List<Coord> maxRectangleStartingAt(int i, List<Coord> openCoords) {
// Now the interesting part:
// Take the maximal width list first: standing for all widths 1, ... max width.
List<Coord> rect = new ArrayList<>();
for (int j = i; j < openCoords.size()
&& openCoords.get(j).y == openCoords.get(i).y
&& openCoords.get(j).x - openCoords.get(i).x == j - i;
++j) {
rect.add(openCoords.get(j);
}
int maxWidth = rect.size();
// Now we can look for next lines below rect.get(0), ... get(maxWidth - 1),
// continuously diminishing maxWidth till 0.
// When we need to diminish maxWidth, we can look wether the original
// maxWidth by (currentY - 1) is a maximum result.
...
}
所以我们走:
+-------------+ 1st rect
| |
| | 2nd rect
| | 3rd rect
| | 4th rect
O(N²) 复杂度的演示:
public static void main(String[] args) {
int[][] openCoords = {
{0, 2},
{1, 0},
{1, 1},
{1, 2},
{2, 0},
{2, 1}
};
new MaxRectFinder().find(openCoords);
}
private int[] maximumTopLeft;
private int[] maximumSize;
private void find(int[][] openCoords) {
maximumTopLeft = null;
maximumSize = new int[2];
for (int topLeftCandidateI = 0; topLeftCandidateI < openCoords.length; ++topLeftCandidateI) {
int yOrig = openCoords[topLeftCandidateI][0];
int xOrig = openCoords[topLeftCandidateI][1];
int y = yOrig;
int x = xOrig + 1;
int j = topLeftCandidateI + 1;
while (j < openCoords.length
&& openCoords[j][0] == y
&& openCoords[j][1] == x) {
++x;
++j;
}
// Now we have a maximum x.
for (;;) {
// Skip after right side on current line:
while (j < openCoords.length
&& openCoords[j][0] == y) {
++j;
}
++y;
// Check for maximum:
if (maximumTopLeft == null || (y - yOrig)*(x - xOrig) > maximumSize[0] * maximumSize[1]) {
maximumSize[0] = y - yOrig;
maximumSize[1] = x - xOrig;
maximumTopLeft = openCoords[topLeftCandidateI];
}
// Skip before left side below origin:
while (j < openCoords.length
&& openCoords[j][0] == y
&& openCoords[j][1] < x) {
++j;
}
if (j >= openCoords.length
|| openCoords[j][0] != y
|| openCoords[j][1] != x) {
break;
}
// Register rectangle part:
int x2 = xOrig;
while (j < openCoords.length
&& openCoords[j][0] == y
&& openCoords[j][1] == x2
&& x2 <= x) {
++x2;
++j;
}
x = x2;
}
}
if (maximumTopLeft == null) {
System.out.println("No solution found, not even 1x1.");
} else {
System.out.printf("At [%d, %d] with size [%d, %d].%n",
maximumTopLeft[0], maximumTopLeft[1], maximumSize[0], maximumSize[1]);
}
}