在给定的 1000 x 1000 阵列中有不同的矩形。中<Figure 1>
,显示为黄色单元格的序列“1”为矩形图案。矩形的最小尺寸<Figure 1>
为 3 x 3,显示为绿色单元格。
矩形内应该至少有一个“0”。
但是,在这个阵列中,也存在不封闭的形状或直线图案。
(数组的初始值为'0',模式由一系列'1'表示。它们不重叠或相互包含。)
除了未闭合的形状或直线之外,找到数组中完整矩形的有效算法是什么?例如上图中完整矩形的数量为 3
这很简单。如果你有n
正方形,你可以计算O(n)
.
假设:
您将需要与输入一样大的额外内存。让我们调用它visited
并用 0 进行初始化。
我们先构造一个辅助函数:
is_rectangle(square s)
from s, go right while you see 1s and visited is 0
mark them as 1 in visited
if less than 3 steps
return 0
then, go down while you see 1s and visited is 0
mark them as 1 in visited
if less than 3 steps
return 0
then, go left while you see 1s and visited is 0
mark them as 1 in visited
if less than 3 steps
return 0
then, go up while you see 1s and visited is 0
mark them as 1 in visited
if then one above is not s
return 0
return 1
该函数基本上沿右下左上的方向跟踪1,并检查是否满足条件(长度至少为3并到达起始位置)。它还标记了访问过的广场。
关于这个函数需要注意的重要一点是,它只有在初始正方形位于左上角时才能正常工作。
现在,问题的解决方案很简单:
num_rectangles = 0
initialize visited to 0 (rows x columns)
for r in rows
for c in columns
if visitied[r][c] or image[r][c] == 0
continue
num_rectangles += is_rectangle(<r,c>)
return num_rectangles
以下是算法的执行方式:
1.坏矩形的失败(和标记)部分
2. 找到(并标记)一个矩形。
3.在单个正方形(垂直线)上失败
4.在一个正方形(垂直线)上失败
5.在一个正方形(垂直线)上失败
6.经过许多类似的步骤,找到下一个矩形。
7. 下一个矩形
8.算法结束
以下 O(n) 算法适用于任何 0/1 值的 2D 矩阵(即,允许相交/重叠的矩形,任意非矩形开放/闭合形状也是如此)。我在这里使用的矩形的定义是“内部完全由 0 个单元组成”(例如,如果一个矩形完全包含另一个矩形,则只会找到内部矩形;如果还应考虑包含矩形,则可以删除每个找到的矩形并重新启动算法)。这是基于观察到每个 0-cell 最多可以在一个 1-rectangle的内部。
我使用约定 x = 0 是最左边的位置,y = 0 是最上面的位置。
如果你的数组中只能有矩形,这相当于一个经典的二进制图像计算问题:只需对连通分量应用标准算法。您只标记 0 的连通分量,并计算它们。
例如,参见http://en.wikipedia.org/wiki/Connected-component_labeling 。这种类型的算法在图像上非常简单,但会占用一些内存(与输入数组大小相同,类型为 short 或 int)。注意连通性:如果您选择 4 连通性,即使缺少某些角,您也会计算封闭的矩形。但该算法比 8 连接更简单。
如果你可以有更复杂的封闭形状,只需添加一个后处理:对于每个连接的组件,计算组件边界框内的像素数(如果两个数字相等,你就有一个矩形)
想了一会儿。我想出了这个方法:
1)消除边缘周围的所有零 - 将它们的值更改为2
2)在2s左右填充矩阵
这只剩下零岛,现在可以测试凸度。所以对于每个岛屿:
3) 在 X 和 Y 中寻找 0 值的范围 - 给你一个潜在的内部矩形
4) 如果内部矩形包含 1 或外部矩形包含 0,则用 2s 填充这个岛,因为它不是凸的(因此不是矩形)
假设您可以找到一个好的洪水填充算法(不像我的那样),这应该可以有效地快速切割搜索空间。
现在是代码(对不起,它是升 C):
using System;
using System.Collections.Generic;
namespace Test
{
class MainClass
{
static private int [,] matrix = new int[,] {
{0,0,0,0,0,0,0,0,1,1,1,1,0,0,0},
{0,1,1,1,1,1,1,0,1,0,0,1,0,1,0},
{0,1,0,0,0,0,1,0,1,0,0,1,0,1,0},
{0,1,0,0,0,0,1,0,1,0,0,1,0,1,0},
{0,1,0,0,0,0,1,0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0,1,0,0,0,0,1,0},
{0,1,1,1,1,1,1,0,1,0,0,1,0,1,0},
{0,0,0,0,0,0,0,0,1,1,1,1,0,0,0},
{0,0,1,1,1,1,0,0,0,0,0,0,0,0,0},
{0,0,1,0,0,1,0,0,1,1,1,0,1,1,0},
{0,0,1,1,1,1,0,0,1,0,1,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0}
};
static private int width = matrix.GetLength(0);
static private int height = matrix.GetLength(1);
private const int DEAD = 2;
private const int RECT = 3;
public static void Main (string[] args)
{
//width = matrix.GetLength(0);
//height = matrix.GetLength(1);
PrintMatrix ();
EliminateFromEdges (DEAD);
PrintMatrix ();
FloodFill (DEAD); // very inefficient - find a better floodfill algorithm
PrintMatrix ();
// test each island of zeros for convexness
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (matrix[i,j] == 0)
{
if (TestIsland(i,j) == false)
{
// eliminate this island as it is not convex
matrix[i,j] = DEAD;
FloodFill(DEAD);
PrintMatrix ();
}
else
{
// flag this rectangle as such
matrix[i,j] = RECT;
FloodFill(RECT);
PrintMatrix ();
}
}
}
}
// We're done, anything flagged as RECT can be expanded to yield the rectangles
PrintMatrix ();
}
// flag any zero at edge of matrix as 'dead'
static private void EliminateFromEdges(int value)
{
for (int i = 0; i < width; i++)
{
if (matrix [i, 0] == 0)
{
matrix [i, 0] = value;
}
if (matrix [i, height - 1] == 0)
{
matrix [i, height - 1] = value;
}
}
for (int j = 1; j < height - 1; j++)
{
if (matrix [0, j] == 0)
{
matrix [0, j] = value;
}
if (matrix [width - 1, j] == 0)
{
matrix [width - 1, j] = value;
}
}
}
// propagte a value to neighbouring cells
static private void FloodFill (int value)
{
bool change_made = true; // set to true to start things off
while (change_made) {
change_made = false;
for (int i = 1; i < width - 1; i++) {
for (int j = 1; j < height - 1; j++) {
if ((matrix [i, j] == 0) &&
((matrix [i - 1, j] == value) ||
(matrix [i + 1, j] == value) ||
(matrix [i, j - 1] == value) ||
(matrix [i, j + 1] == value))) {
matrix [i, j] = value;
change_made = true;
}
}
}
}
}
static private bool TestIsland (int x, int y)
{
// find convex extend of island
int x2 = x;
int y2 = y;
while (matrix[++x2, y] == 0);
x2--;
while (matrix[x,++y2] == 0);
y2--;
// check inner cells (want all zeroes)
for (int i = x; i <= x2; i++)
{
for (int j = y; j <= y2; j++)
{
if (matrix[i,y] != 0)
{
return false;
}
}
}
// check surrounding cells (want all ones)
x--; y--;
x2++; y2++;
for (int i = x; i <= x2; i++)
{
if ((matrix[i,y] != 1) || (matrix[i,y2] != 1))
{
return false;
}
}
for (int j = y + 1; j <= y2 - 1; j++)
{
if ((matrix[x,j] != 1) || (matrix[x2,j] != 1))
{
return false;
}
}
return true;
}
// for debug purposes
static private void PrintMatrix ()
{
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
switch(matrix[i,j])
{
case DEAD:
Console.Write("-");
break;
case RECT:
Console.Write("+");
break;
default:
Console.Write(matrix[i,j]);
break;
}
}
Console.WriteLine();
}
Console.WriteLine();
}
}
}
此代码的输出
000000001111000
011111101001010
010000101001010
010000101001010
010000101000010
010000101000010
011111101001010
000000001111000
001111000000000
001001001110110
001111001010000
000000001110000
--------1111---
-1111110100101-
-1000010100101-
-1000010100101-
-1000010100001-
-1000010100001-
-1111110100101-
-0000000111100-
-0111100000000-
-0100100111011-
-0111100101000-
--------111----
--------1111---
-111111-1--1-1-
-100001-1--1-1-
-100001-1--1-1-
-100001-1----1-
-100001-1----1-
-111111-1--1-1-
--------1111---
--1111---------
--1001--111-11-
--1111--101----
--------111----
--------1111---
-111111-1--1-1-
-1++++1-1--1-1-
-1++++1-1--1-1-
-1++++1-1----1-
-1++++1-1----1-
-111111-1--1-1-
--------1111---
--1111---------
--1001--111-11-
--1111--101----
--------111----
--------1111---
-111111-1--1-1-
-1++++1-1--1-1-
-1++++1-1--1-1-
-1++++1-1----1-
-1++++1-1----1-
-111111-1--1-1-
--------1111---
--1111---------
--1++1--111-11-
--1111--101----
--------111----
--------1111---
-111111-1--1-1-
-1++++1-1--1-1-
-1++++1-1--1-1-
-1++++1-1----1-
-1++++1-1----1-
-111111-1--1-1-
--------1111---
--1111---------
--1++1--111-11-
--1111--1+1----
--------111----
--------1111---
-111111-1--1-1-
-1++++1-1--1-1-
-1++++1-1--1-1-
-1++++1-1----1-
-1++++1-1----1-
-111111-1--1-1-
--------1111---
--1111---------
--1++1--111-11-
--1111--1+1----
--------111----
这就是我的想法,可能资源效率很低。不知道那件事。
1
秒。boolean
,它们的格式应该是这样的。100..001
(假设你可以做所有的boolean
操作)1
s。