有人知道在2d数组中找到“形状”的算法吗?

时间:2013-06-17 13:51:35

标签: algorithm graph

我们来看看这张地图,其中'#'表示一个正方形和'。'说明了一个免费的广场:

1 . # # # . .
2 . # . . # .
3 # . . . . #
4 . # # # . .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6

现在,如果我在正方形4,5中放置一个'#',那么这个区域就会被“填充”:

1 . # # # . .
2 . # # # # .
3 # # # # # #
4 . # # # # .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6

那么,找到“有限广场”的最佳方法是什么,我可以在哪里开始填充或填充有限区域的其他填充算法?

5 个答案:

答案 0 :(得分:6)

如果您可以将问题转换为图表,那么您要查找的是识别连接的组件。如果连接的组件不包含作为边界边的边,那么您已找到需要填充的区域。

如果我这样定义图形:

G = (V, E)
V = {r1, r2, r3, r4, r5, r6, c1, c2, c3, c4, c5, c6}
E = {(u, v) | u, v are elements of V && the cell(u, v) is not taken}

现在运行DFS以查找所有已断开连接的树。算法:

for each u in V:
    color[u] = white

for each u in V:
    if color[u] == white:
        contains_boundary_edge = False
        DFS-visit( u, contains_boundary_edge )

        if not contains_boundary_edge:
            Flood-fill( u )

DFS-visit( u, contains_boundary_edge ):
    color[u] = gray
    for each v in adjacent( u ):
        if color[v] == white:
            if edge(u, v) is a boundary edge: // Can be easily identified if one of u, v is start or end row/col.
                contains_boundary_edge = True

            DFS-visit( v, contains_boundary_edge )

    color[u] = black

答案 1 :(得分:3)

我认为如果我们将每个#视为点x,那么这个问题可以简化为凸包问题,然后凸包给我们所有#不存在的x,y

http://en.wikipedia.org/wiki/Convex_hull

我会尝试在休闲时编码..

答案 2 :(得分:2)

你可以通过处理每个'。'来攻击它。节点

定义:A'。'如果不存在从节点到地图边界的路径,则封闭节点。

如果您同意上述定义,则算法将维护图表'。'节点,相邻节点连接在一起。

每次将某个节点更改为“#”时,请将其从此图表中删除,然后检查每个剩余的“。”节点,以查看是否存在从其到地图边界上的某个节点的路径。

根据地图的大小,您需要尝试各种优化来限制每回合执行的路径搜索次数。

答案 3 :(得分:1)

如果您将此地图建模为图表,并且每个方块都连接到其四个邻居,则可以使用bridge finding algorithm找到所需的方块。

请注意,此模型有时会为您提供几个子图,因此它可能会在边界周围产生许多误报,因为添加#肯定会将某些节点与其余节点分开。要解决这个问题,您可以在图表周围填充两个级别的方块,这样就不会有单个#将边界节点与其他节点分开。

@ svick的评论启发了这种方法。

答案 4 :(得分:1)

我会从拾取的方块的每个邻居开始,并尝试“逃离”到网格的边界。同时,标记路径后跟'X'。如果你可以逃脱:撤消每个'X'。如果无法逃脱,请将每个'X'替换为'#'。我在Java中做了一个例子,如下所示。

int W, H;   
char[][] input;
final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

public void handle(int x, int y) {
    // try each neihgbor
    for (int[] d : directions) {
        if (canEscape(input, x, y)) {
            // if we can escape, the path found shouldn't be filled
            // so replace the Xes by '.';
            handleXes(input, false);                
        } else {
            // if we cannot escape, this is a closed shape, so
            // fill with '#'
            handleXes(input, true);
        }
        // note that this can be written more concisely as
        // handleXes(input, !canEscape(input, x, y));
    }
}    

public boolean canEscape(char[][] grid, int x, int y) {
    if (isEscape(grid, x, y))
        return true

    if (isValid(grid, x, y)) {
        // mark as visited
        grid[x][y] = 'X';
        // try each neighbor
        for (int[] d : directions) {
            if (canEscape(grid, x+d[0], y+d[1]))
                return true;
        }
    }

    return false;
}

public boolean isValid(char[][] grid, int x, int y) {
    return 0 <= x && x < W && 0 <= y && y < H && grid[x][y] == '.';
}

public boolean isEscape(char[][] grid, int x, int y) {
    return (0 == x || x == W-1 || 0 == y || y == H-1) && grid[x][y] == '.';
}   

public void handleXes(char[][] grid, boolean fill) {
    for (int x = 0; x < W; x++)
        for (int y = 0; y < H; y++)
            if (grid[x][y] == 'X')
                grid[x][y] = fill ? '#' : '.';
}