一个用于返回最大可能矩形块中可用空间的算法是什么?

12

算法

考虑以下布局:

+-------------+
|             |
|             |
|   +--+      |
|   |##|      |
|   |##|      |
|   +--+------+
|      |######|
|      |######|
+------+------+

黑色部分是已占用的空间。现在我需要一个算法,返回最大的剩余矩形空间。(按从上到下、从左到右的顺序排列。)

像这样:

1                 2          3            4
+-------------+   +----      -------+
|#############|   |###        ######|
|#############|   |###        ######|
|   +--+      |   |###+      +######|
                  |###|      |######|
                  |###|      |######|
                  |###+      +------|     |   +--+
                  |###                    |######|
                  |###                    |######|
                  +----                   +------+

输入

包围容器的宽度和高度。(我的代码中的页面。)

一组已占用的矩形列表。它们可以采用您喜欢的任何形式。例如 (x,y,width,height) 或 (x1,y1,x2,y2)

我正在处理浮点数,因此更喜欢数学解决方案。


1
算法以最大可能的矩形块返回可用空间,对吧? - Amarghosh
您的算法的输入究竟是什么? - Romain
1
@MaR:是的,它们可以,但这是可以轻松移除的东西。因此你不必考虑它。 - Georg Schölly
@GeorgSchölly,您能否分享更详细的算法来解决上述问题,并说明如何消除最大矩形之间的重叠? - EmptyData
@EmptyData:非常抱歉,我不记得我当时尝试解决问题的确切细节,那已经超过5年了。我已经接受了一个答案,所以我想那解决了我的问题。 - Georg Schölly
显示剩余2条评论
8个回答

6
从您的示例中可以看出,您并不需要排除重叠部分(例如,1和2具有相同的左上角),因此也许这会满足您的需求:
  1. 根据已占用空间确定的角落将空间划分为矩形。

  2. 通过从这些角落延伸线段到整个空间的边缘来形成“基本矩形”。

  3. 使用任何系统化的顺序(例如从上到下,从左到右):

    3.1.选择一个基本矩形,并尽可能地与其他具有公共边的基本矩形一起扩展。

    3.2.形成所有(唯一的)这样扩展矩形的集合。

请注意,这是基于步骤2中的“基本矩形”进行搜索/构建的,而不是逐点遍历整个空间,因此性能应该更好。

我知道这是一个旧答案,但你能否澄清一下你的答案?我已经读了几十遍了,仍然无法理解这个过程 :)。我主要对“基于角落的矩形”和“用其他基本矩形扩展它”这些术语的含义有问题。 - Ryan Pergent
@Ryan Pergent:类似以下的内容:+-------------+ |a |b |c | | | | | |---+--+------| |d |##|e | | |##| | |---+--+------+ |f |g |######| | | |######| +------+------+字母标记基本矩形。矩形“ab”、“abc”、“ad”和“adf”都是基于公共边长扩展矩形“a”的。 - joel.neely

1

抱歉我用代码写了:

for(int y = 0; y < rows; y++){

  for(int x = 0; x < columns; x++){
     // (x, y) would be the current space

     if(checkEmptySpace(x,y)){
       // empty space found

     }

  }

}

这是最简单和直接的方法。但坏处是必须循环遍历所有空间,可能导致低效率。

改进:

  1. (STEP1) 在行为空的情况下循环遍历所有行
  2. (STEP1) 当在行中发现有占用空间时停止
    1. (STEP1) 仅在第一次时将当前行的值存储到TOP_EMPTY中
  3. (STEP2) 如果剩余行数>列数,则转到步骤8
  4. (STEP2) 循环遍历剩余行
  5. (STEP2) 计算占用空间前面的空间
  6. (STEP2) 计算占用空间后面的空间
  7. (STEP2) 循环结束
  8. (STEP2) 转到13
  9. (STEP2) 循环遍历列
  10. (STEP2) 跳过TOP_EMPTY行
  11. (STEP2) 计算列中占用空间前面的空间
  12. (STEP2) 计算列中占用空间后面的空间
  13. (STEP2) 循环结束
  14. (STEP3) 计算顶部的空间,即TOP_EMPTY * 列数
  15. 完成。

1
char mark = 'A';
for(i from 0 to rows)
    colstart = 0;
    while(colstart = next empty col in ith row)
        width = 0;
        for(j from colstart to columns)
        {
            if(empty)
                width++;
            else 
                break;
        }
        if(width == 0)
            continue
        for(n from colstart to colstart + width)
            for(m from i to rows)
                if(!empty)
                    break;
            if(m != i)
                set the rectangle from i, colstart to m - 1, colstart + width 
                with mark char and increment mark;

更新:Java 代码。

public class Program
{
    public static char occuppied;
    public static char vacant;
    public static char mark;
    public static void main(String[] args)
    {
        int rows = 7;
        int cols = 11;
        mark = 'A';
        occuppied = '#';
        vacant = '-';
        char[][] matrix = new char[rows][cols];
        setRect(matrix, vacant, 0, 0, rows, cols);
        setRect(matrix, occuppied, 3, 3, 2, 2);
        setRect(matrix, occuppied, 5, 5, 2, 6);

        print(matrix);
        for(int i = 0; i < rows; i++)
        {
            int colstart = 0;
            while((colstart = nextEmptyCol(matrix[i], colstart)) != -1)
            {
                int width = 0;
                for(int j = colstart; j < cols; j++)
                {
                    if(matrix[i][j] == vacant)
                        width++;
                    else
                        break;
                }
                if(width == 0)
                    continue;
                int height = 1;
                outer:
                for(; height + i < rows; height++)
                    for(int n = 0; n < width; n++)
                    {
                        if(matrix[i + height][colstart + n] == occuppied)
                            break outer;
                    }
                System.out.println("width = " + width + ", height = " + height);
                setRect(matrix, mark, i, colstart, height, width);
                print(matrix);
                mark++;
            }
        }
    }
    public static void setRect(char[][] matrix, char c, int startrow, int startcol, int numrows, int numcols)
    {
        for(int i = 0; i < numrows; i++)
            for(int j = 0; j < numcols; j++)
                matrix[startrow + i][startcol + j] = c;
    }
    public static void print(char[][] matrix)
    {
        int rows = matrix.length;
        int cols = matrix[0].length;
        for(int i = 0; i < rows; i++)
        {
            for(int j = 0; j < cols; j++)
                System.out.print(matrix[i][j] + " ");
            System.out.println();
        }
        for(int i = 0; i < cols; i++)
            System.out.print("==");
        System.out.println();
    }
    public static int nextEmptyCol(char[] row, int start)
    {
        for(int i = start; i < row.length; i++)
            if(row[i] == vacant)
                return i;
        return -1;
    }
}

我正在使用浮点数。因此,我不能迭代所有像素。 - Georg Schölly
糟糕...在编写Java代码后才看到这个评论...但还是加上了。 - Amarghosh

0

我认为仅考虑角落并不能得到最佳的数学结果。

例如,在下面的图像中,“r”矩形是最佳匹配,但并不始于任何一个角落。

+-------------+
|    rrrr     |
|+--+rrrr +--+|
||##|rrrr |##||
||##|rrrr |##||
||##|rrrr |##||
||##|rrrr |##||
|+--+rrrr +--+|
|    rrrr     |
+-------------+

在这种情况下,是的,我需要左上角的矩形。 - Georg Schölly
你没有理解,用“r”制作的矩形不是一个被占用的矩形。它是更大的矩形。 - Filipizaum

0
  1. 开始
  2. 设置行=0,列=0
  3. 如果是空闲空间:
    1. 获取最大的自由矩形,从水平方向开始
  4. 如果不是,并且在最后一列,而且不在末尾,行+1,列=0并转到3
  5. 否则,如果不在最后一列,列+1并转到3
  6. 否则结束

(请注意,3.1是相同的算法,只是自由/阻塞反转,并具有不同的坐标)


0

0

这是我在完全相同情况下使用的算法:

这将返回一个空白矩形列表

  1. 按照从左上到右下的顺序,在已知障碍物列表中对其进行排序
  2. 创建第一个空白区域矩形,该矩形占据您要检查的整个区域
  3. 对于每个障碍物(O):
    1. 对于与O重叠的每个空白区域:
      1. 删除空白矩形(ESR)
      2. 如果 ESR.left < O.left,则创建新的空白矩形(NESR),并将其加入其他障碍物必须检查重叠的ESR列表中。
      3. 如果 ESR.right > O.right,则创建新的ESR,NESR.left = O.right
      4. 如果 ESR.bottom < O.bottom,则创建新的ESR,NESR.top = O.bottom
      5. 如果 ESR.top > O.top,则创建新的ESR,NESR.bottom = O.top

注意:这是一系列的If语句,而不是else if。这意味着您需要为每个重叠创建最多4个新的ESR。

解释

  • 我们创建一个巨大的“空白矩形”,然后检查每个已知障碍物是否与其重叠(显然会,因为此矩形覆盖了所有内容)。
  • 第一个障碍物将把第一个空白空间矩形分成最多4个较小的矩形。限制不重叠。
  • 然后,第二个障碍物将检查它是否与任何新创建的空白空间矩形重叠。它重叠的每个矩形都会进一步分裂成最多4个新矩形。其中没有一个会重叠障碍物1或障碍物2。
  • 我们继续这样做,直到检查完每个障碍物。
  • 我们最终得到了一份不与任何障碍物重叠的空白空间列表。但我们肯定会有重叠。

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接