UVaOJ 705 - Slash Maze

——by A Code Rabbit


Description

输入一张迷宫图,全部由 '/'、'\' 组成。
这样,迷宫就变成一个斜线迷宫。


因为输入全为 '/' 、'\' ,因此,这个迷宫将由全部宽度为1的路径组成。
要求输出迷宫中有几个回环,即封闭的路径有几条,其中最长的有多长。


Types

Date Structure :: Graphs


Analysis

经典的斜线迷宫题。

可用 FloodFill 解决。

首先知道,没有封闭的路径,必然将通往图的外面。

所以只要从图的边界开始 FloodFill,把不满足条件的排除后。

再对每一个点去 FloodFill 即可求出所要的解。

而对于斜线的处理,有三种方法:

1. 九分法:将所有的格子都扩大成 9 * 9 的格子,例如

‘/’ 就会变成


然后只要用普通的 FloodFill 对每格上下左右四个方向的 DFS 就可以。


2. 四分法:将所有的格子扩大成 4 * 4 的格子,例如

‘/’ 就会变成


然后依然是对每个格子向八个方向 FloodFill ,但是要注意,在 2 * 2 格子中的某点向周围Flood 的时候,只能到达上下左右的 2 * 2格子。

反之如果 FloodFill 后依然在原先的 2 * 2格子,或者对角线方向的 2 * 2 格子的,将会是穿过了 '/' 的非法情况,需要排除掉。


3. 光线反射法:模拟一条光线在迷宫中照射,继续看图:

于每个格子,光线可能从四个方向射进来。

在 FloodFill 的 时候 ,判断光线来的方向,找到下一个 FloodFill 的格子 即可。

三种方法,第一种较为简单,但是要将原图长宽各扩大三倍,得到一张原图九倍大的新图。

而第二种方法,只要将原图扩大四倍,但是要判断要FloodFill的格子是否可以FloodFill。

而第三种方法,较为复杂,对光线进来的四个方向都要判断反射出去的方向,可以用一个 enum 配合 const 数组来映射,好处就是不用扩大原图,时间复杂度相对前两种,常数较低。


Solution

1. 九分法
// UVaOJ 705
// Slash Maze
// by A Code Rabbit

#include <cstdio>

struct Change {
    int x;
    int y;
};

Change CHANGE[] = {
              {-1, 0},
    { 0, -1},          { 0, 1},
              { 1, 0},
};

const int LIMITS_W = 1000;
const int LIMITS_H = 1000;

int num_case = 0;

char maze[LIMITS_H][LIMITS_W];
int w, h;
int w_maze, h_maze;

void EnlargeAndSave(int x, int y, char ch);

int FloodFill(int x, int y);

int main() {
    while (scanf("%d%d", &w, &h)) {
        getchar();
        // Exit.
        if (!w && !h) {
            break;
        }
        // Inputs.
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                EnlargeAndSave(i, j, getchar());
            }
            getchar();
        }
        w_maze = w * 3;
        h_maze = h * 3;
        // DFS: Remove grids of the maze without cycles.
        for (int i = 0; i < h_maze; ++i) {
            FloodFill(i, 0);
            FloodFill(i, w_maze - 1);
        }
        for (int i = 0; i < w_maze; ++i) {
            FloodFill(0, i);
            FloodFill(h_maze - 1, i);
        }
        // DFS: Search and compete the maze with cycles.
        int max = 0;
        int sum = 0;
        for (int i = 0; i < h_maze; ++i) {
            for (int j = 0; j < w_maze; ++j) {
                int result = FloodFill(i, j) / 3;
                if (result) {
                    max = result > max ? result : max;
                    ++sum;
                }
            }
        }
        // Outputs.
        printf("Maze #%d:\n", ++num_case);
        if (sum) {
            printf("%d Cycles; the longest has length %d.\n", sum, max);
        } else {
            printf("There are no cycles.\n");
        }
        printf("\n");
    }
    
    return 0;
}

void EnlargeAndSave(int x, int y, char ch) {
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            maze[x * 3 + i][y * 3 + j] = ' ';
        }
    }
    if (ch == '/') {
        maze[x * 3][y * 3 + 2] = '#';
        maze[x * 3 + 1][y * 3 + 1] = '#';
        maze[x * 3 + 2][y * 3] = '#';
    } else {
        maze[x * 3][y * 3] = '#';
        maze[x * 3 + 1][y * 3 + 1] = '#';
        maze[x * 3 + 2][y * 3 + 2] = '#';
    }
}

int FloodFill(int x, int y) {
    // Exit.
    if (x < 0 || x >= h_maze
     || y < 0 || y >= w_maze) {
        return 0;
    }
    if (maze[x][y] != ' ') {
        return 0;
    }
    // Continue.
    maze[x][y] = '#';
    int sum = 1;
    for (int i = 0; i < 4; ++i) {
        sum += FloodFill(x + CHANGE[i].x, y + CHANGE[i].y);
    }
    return sum;
}

2. 四分法
// UVaOJ 705
// Slash Maze
// by A Code Rabbit

#include <cstdio>

struct Change {
    int x;
    int y;
};

Change STRAIGHT_CHANGE[] = {
              {-1, 0},
    { 0, -1},          { 0, 1},
              { 1, 0},
};

Change SLASH_CHANGE[] = {
    {-1, -1},          {-1, 1},
    
    { 1, -1},          { 1, 1},
};

const int LIMITS_W = 500;
const int LIMITS_H = 500;

int num_case = 0;

char maze[LIMITS_H][LIMITS_W];
int w, h;
int w_maze, h_maze;

void EnlargeAndSave(int x, int y, char ch);

bool canArrive(int x, int y, Change change);
int FloodFill(int x, int y);

int main() {
    while (scanf("%d%d", &w, &h)) {
        getchar();
        // Exit.
        if (!w && !h) {
            break;
        }
        // Inputs.
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                EnlargeAndSave(i, j, getchar());
            }
            getchar();
        }
        w_maze = w * 2;
        h_maze = h * 2;
        // DFS: Remove grids of the maze without cycles.
        for (int i = 0; i < h_maze; ++i) {
            FloodFill(i, 0);
            FloodFill(i, w_maze - 1);
        }
        for (int i = 0; i < w_maze; ++i) {
            FloodFill(0, i);
            FloodFill(h_maze - 1, i);
        }
        // DFS: Search and compete the maze with cycles.
        int max = 0;
        int sum = 0;
        for (int i = 0; i < h_maze; ++i) {
            for (int j = 0; j < w_maze; ++j) {
                int result = FloodFill(i, j);
                if (result) {
                    max = result > max ? result : max;
                    ++sum;
                }
            }
        }
        // Outputs.
        printf("Maze #%d:\n", ++num_case);
        if (sum) {
            printf("%d Cycles; the longest has length %d.\n", sum, max);
        } else {
            printf("There are no cycles.\n");
        }
        printf("\n");
    }
    
    return 0;
}

void EnlargeAndSave(int x, int y, char ch) {
    maze[x * 2][y * 2] = ch == '/' ? ' ' : '#';
    maze[x * 2][y * 2 + 1] = ch == '/' ? '#' : ' ';
    maze[x * 2 + 1][y * 2] = ch == '/' ? '#' : ' ';
    maze[x * 2 + 1][y * 2 + 1] = ch == '/' ? ' ' : '#';
}

bool canArrive(int x, int y, Change change) {
    int x_after_change = x % 2 + change.x;
    int y_after_change = y % 2 + change.y;
    if (0 <= x_after_change && x_after_change < 2
     && 0 <= y_after_change && y_after_change < 2) {
        return false;
    } else
    if ((x_after_change < 0 || x_after_change >= 2)
     && (y_after_change < 0 || y_after_change >= 2)) {
        return false;
    } else {
        return true;
    }
    printf("\n");
}

int FloodFill(int x, int y) {
    // Exit.
    if (x < 0 || x >= h_maze
     || y < 0 || y >= w_maze) {
        return 0;
    }
    if (maze[x][y] != ' ') {
        return 0;
    }
    // Continue.
    maze[x][y] = '.';
    int sum = 1;
    for (int i = 0; i < 4; ++i) {
        sum += FloodFill(x + STRAIGHT_CHANGE[i].x, y + STRAIGHT_CHANGE[i].y);
    }
    for (int i = 0; i < 4; ++i) {
        if (canArrive(x, y, SLASH_CHANGE[i])) {
            sum += FloodFill(x + SLASH_CHANGE[i].x, y + SLASH_CHANGE[i].y);
        }
    }

    return sum;
}

3. 光线反射法
// UVaOJ 705
// Slash Maze
// by A Code Rabbit

#include <cstdio>
#include <cstring>


enum Direction {
    UP = 0,
    DOWN = 1,
    LEFT = 2,
    RIGHT = 3,
};

struct Change {
    int x;
    int y;
};

const Direction DIRECTION[] = {
    UP,
    DOWN,
    LEFT,
    RIGHT,
};
// Form four directions what are UP, DOWN, LEFT, RIGHT.
const Change CHANGE[] = {
    {-1,  0},
    { 1,  0},
    { 0, -1},
    { 0,  1},
};

// Form four directions what are UP, DOWN, LEFT, RIGHT.
const Direction REFLEX_SLASH[] = {
    LEFT,
    RIGHT,
    UP,
    DOWN,
};
const Direction REFLEX_BACKSLASH[] = {
    RIGHT,
    LEFT,
    DOWN,
    UP, 
};

const int LIMITS_W = 100;
const int LIMITS_H = 100;

int num_case = 0;

char maze[LIMITS_W][LIMITS_H];
int w, h;

bool is_visited[LIMITS_W][LIMITS_H][4];

Direction Opposite(Direction direction);
int FloodFill(int x, int y, Direction direction);

int main() {
    while (scanf("%d%d", &w, &h)) {
        getchar();
        // Exit.
        if (!w && !h) {
            break;
        }
        // Inputs.
        for (int i = 0; i < h; ++i) {
            gets(maze[i]);
        }
        // DFS: Remove grids of the maze without cycles.
        memset(is_visited, false, sizeof(is_visited));
        for (int i = 0; i < h; ++i) {
            FloodFill(i, 0, LEFT);
            FloodFill(i, w - 1, RIGHT);
        }
        for (int i = 0; i < w; ++i) {
            FloodFill(0, i, UP);
            FloodFill(h - 1, i, DOWN);
        }
        //Show();
        // DFS: Search and compete the maze with cycles.
        int max = 0;
        int sum = 0;
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                for (int k = 0; k < 4; k++) {
                    int result = FloodFill(i, j, DIRECTION[k]);
                    if (result) {
                        max = result > max ? result : max;
                        ++sum;
                    }
                }
            }
        }
        // Outputs.
        //Show();
        printf("Maze #%d:\n", ++num_case);
        if (sum) {
            printf("%d Cycles; the longest has length %d.\n", sum, max);
        } else {
            printf("There are no cycles.\n");
        }
        printf("\n");
    }


    return 0;
}

Direction Opposite(Direction direction) {
    if (direction == LEFT) {
        return RIGHT;
    } else
    if (direction == RIGHT) {
        return LEFT;
    } else
    if (direction == UP) {
        return DOWN;
    } else
    if (direction == DOWN) {
        return UP;
    }
}

int FloodFill(int x, int y, Direction direction) {
    // Exit.
    if (x < 0 || x >= h
     || y < 0 || y >= w) {
        return 0;
    }
    if (is_visited[x][y][direction]) {
        return 0;
    }
    is_visited[x][y][direction] = true;
    // Continue.
    Direction direction_leave;
    if (maze[x][y] == '/') {
        direction_leave = REFLEX_SLASH[direction];
    } else {
        direction_leave = REFLEX_BACKSLASH[direction]; 
    }
    is_visited[x][y][direction_leave] = true;
    return 1 + FloodFill(x + CHANGE[direction_leave].x,
                         y + CHANGE[direction_leave].y,
                         Opposite(direction_leave));
}



下载PDF

参考资料:wjjayo博客  crystal_yi博客  Penseur博客

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值