找到网格中最大的矩形,它位于边界上并包含点

时间:2015-04-13 18:11:48

标签: algorithm grid rectangles

我的代码遍历了一个免费网格"阻止"位置;并找到最大的矩形。

这很好用。但是,我需要能够找到具有更具体标准的矩形。请考虑以下事项:

00X0000000
000000####
##########
####000000
0000000000
0000000000

在此图中; 0标记可用位置,#标记占用位置,X标记所需点。

我现在的代码将找到网格的右下区域,因为它确实是最大的矩形。但是,我想要的答案包含X位置(左上角的矩形)。

我不知道如何添加这个额外的标准。我试着跟踪坐标;抛弃不含X的结果;但这并不是一直有效。在某些情况下,我会得到比想要的小的矩形(可能是因为我穿过网格的顺序)

我发现的所有算法似乎只能找到最大的矩形和矩形的坐标。我无法找到任何能够可靠地排除不包含特定点的结果。

有人能指出我正确的方向,和/或提供实施吗?

修改:    由于评论不能有换行符,我需要使用此区域来解释shole实现的问题。它不适用于以下情况:

#0#X
###0
#000
####
0#00
0000
0000

在上面的示例中,返回的坐标形成如下:

0#X
##0
000

答案应该是1x3,而不是3x3

另一个失败的例子:

0#X#
0000
00##
000#
0000

返回:

#X
00

在这种情况下,返回的答案应该是1x2区域,而不是它认为是2x2。

在某些情况下,它返回的区域大于整个网格的区域,坐标超出有效坐标范围。

这适用于SOMETIMES,但它通常不正确而且不正确。它总是返回包含该点的区域;只是错误的区域/坐标。

1 个答案:

答案 0 :(得分:1)

有趣的问题,这是我的O(R * C)算法,R =#rows,C = #columns

这个想法很简单,蛮力将每个点作为潜在的矩形的底部边界,然后尝试尽可能高地爬上以获得最大高度,然后获得该垂直线的最大左右距离

enter image description here

这会测试所有潜在的矩形,但我们必须快速完成。 我们通过逐行动态编程为每个(i,j)计算最大高度,最大左右距离。

然后我们可以测试X是否在这个潜在的矩形内,如果是的话,我将无限大偏移添加到这个矩形的区域,它足够大,任何矩形都不包含X将小于它,而其他矩形,如果包含X并且大于它,我们仍然可以选择那个矩形。

在算法结束后,我们得到最大 Area + Offset ,我们从中减去偏移并将区域恢复。

如果您运行代码(C ++)并查看下面的日志,并通过以下输入更容易理解:

6 10
00X0000000
000000####
##########
####000000
0000000000
0000000000

#include<bits/stdc++.h>
using namespace std;

char w[20][20];
int tl[20][20] = {0}, tr[20][20] = {0}, h[20][20] = {0}, l[20][20]={0}, r[20][20]={0};
int ans, R,C, xr,xc, ar1, ac1, ar2, ac2; 

int largestRect()
{
    int area = 0;
    
    for(int i=0; i<20;i++) for(int j=0; j<20;j++) l[i][j] = r[i][j] = 1<<28;
    
   	for(int i=1; i<=R;i++){
   	    for(int j=1; j<=C;j++)
   		    if(w[i][j]!='#') tl[i][j] = tl[i][j-1]+1;
   			else tl[i][j] = 0;
 
   		for(int j=C; j>=1; j--)
   			if(w[i][j]!='#') tr[i][j] = tr[i][j+1]+1;
   			else tr[i][j] = 0;
   		
   		for(int j=1; j<=C; j++)
   			if(w[i][j]!='#') h[i][j] = h[i-1][j]+1;
   			else h[i][j] = 0;
   		
   		for(int j=1; j<=C; j++)
   			if(w[i][j] != '#') l[i][j] = min(tl[i][j], l[i-1][j]);
   		
   		for(int j=1; j<=C; j++)
   			if(w[i][j] != '#') r[i][j] = min(tr[i][j], r[i-1][j]);
   	
   		
   		for(int j=1; j<=C;j++){
   			int offset = 0;
   			if((r[i][j]+l[i][j]-1)*h[i][j] > 0){
   			    if(xr >= i-h[i][j]+1 && xr <= i && xc >= j-l[i][j]+1 && xc <= j+r[i][j]-1) offset = 1<<28;
   				printf("Top left = (%d,%d)  Bottom right = (%d,%d)\n", i-h[i][j]+1, j-l[i][j]+1,i, j+r[i][j]-1);
   				printf("Area = %d\n", (r[i][j]+l[i][j]-1)*h[i][j]);
   				printf("Included X? %d\n", xr >= i-h[i][j]+1 && xr <= i && xc >= j-l[i][j]+1 && xc <= j+r[i][j]-1 );
   				printf("Area with offset = %d\n\n", (r[i][j]+l[i][j]-1)*h[i][j] + offset);
   					
   				if(area <= (r[i][j]+l[i][j]-1)*h[i][j] + offset){
   					area = max(area, (r[i][j]+l[i][j]-1)*h[i][j] + offset);
   						ar1 = i-h[i][j]+1; ac1 = j-l[i][j]+1; ar2 = i; ac2 = j+r[i][j]-1;
   				}
   			}
   		}
   	}
   
    return area;
}

int main(){
	scanf("%d %d", &R, &C);
	
	for(int i=0; i<20;i++) for(int j=0; j<20;j++) w[i][j] = '#';
	
	for(int i=1; i<=R; i++){
		getchar();
		for(int j=1; j<=C; j++){
			w[i][j] = getchar();	
			if(w[i][j] == 'X'){ xr = i; xc = j;}
		} 
	}
	
	ans = largestRect() - (1<<28);

	printf("Largest Rect Containing X is (%d,%d) to (%d,%d), with area = %d\n", ar1, ac1, ar2, ac2, ans);
	return 0;	
}

编辑部分:详细解释的算法/代码

首先,这是我们的O(R * C)算法的概要:

  1. 在网格中强制每个空点,将其用作我们潜在的最大空矩形的下边界点。这是O(R * C)
  2. 对于每个点,我想尽可能向上,让我们定义 h [i] [j] 是数组,意味着你可以从(i,j)开始到达的最高高度
  3. 现在我想知道我可以向左和向右走多远,在此垂直段范围内
  4. 我得到一个高度,我得到最长的左右我可以达到这个高度,所以我当然可以计算这个矩形的区域
  5. 获取所有矩形区域的最大值,重复步骤1-4
  6. 然后我们将看到如何在算法中执行步骤2-3,我们将使用动态编程

    让我们定义几个数组:

    h[i][j] := the highest height you can reach starting at (i,j)
    tr[i][j] := the longest distance to right you can reach starting at (i,j)
    tl[i][j] := the longest distance to left you can reach starting at (i,j)
    

    应该能够看到这三个阵列可以使用O(R * C)预先计算

    h[i][j] = h[i-1][j]+1  if(i,j) is empty, else h[i][j] = 0
    similarly
    tr[i][j] = tr[i][j+1]+1 if(i,j) is empty, else tr[i][j] = 0
    tl[i][j] = tl[i][j-1]+1 if(i,j) is empty, else tl[i][j] = 0
    

    现在我们再定义两个数组

    l[i][j] := the longest distance to right you can reach starting from 
    line (i,j) and (i-h[i][j]+1,j) (That's simply just put h[i][j] into consideration)
    

    与r [i] [j]类似,请确保您理解tl [i] [j]和l [i] [j](tr [i] [j]和r [i]之间的区别[ j])

    现在l [i] [j]和r [i] [j]也可以使用O(R * C)预先计算!

    一点不同是他们需要h [i] [j]和tr [i] [j](或tl [i] [j])来计算它,

    所以h [i] [j],tl [i] [j],tr [i] [j]必须在l [i] [j]和r [i] [j]之前计算。

    l[i][j] = min(l[i-1][j], tl[i][j]) if(i,j) is empty, else l[i][j] = tl[i][j]
    r[i][j] = min(r[i-1][j], tr[i][j]) if(i,j) is empty, else r[i][j] = tr[i][j]
    
      

    您可以认为它说的是以下陈述:

         

    l [i] [j]是最小tl [X] [j] ,在[i-h [i] [j] +1,i]中的X

         

    所以l [i] [j]是(i,j)到最大高度的最大距离

    与r [i] [j]

    类似

    所以我的代码正在使用两个for循环来完成上面描述的动态编程,它似乎很复杂,因为我在使用之前没有预先计算它们

    我计算这些数组并使用它们来计算矩形区域即时。话虽如此,你总是可以先预先计算所有这些数组,然后再计算最大面积,时间复杂度仍为O(R * C)