03. 迷宫回溯问题

一、生成迷宫

/**
 * @brief 生成迷宫
 * 
 * @param Width 迷宫的宽
 * @param Height 迷宫的高
 * @param MaxBrickCount 迷宫中最多有多少个砖块
 * @return int** 生成的迷宫
 */
int ** CreateMaze(int Width, int Height, int MaxBrickCount)
{
    int ** Maze = (int **)calloc(Height, sizeof(int *));
    for (int i = 0; i < Height; i++)
    {
        Maze[i] = (int *)calloc(Width, sizeof(int));
    }
  
    // 上下两行置为1
    for (int i = 0; i < Width; i++)
    {
        Maze[0][i] = 1;
        Maze[Height - 1][i] = 1;
    }
  
    // 左右两列置为1
    for (int i = 0; i < Height; i++)
    {
        Maze[i][0] = 1;
        Maze[i][Width - 1] = 1;
    }

    srand(time(NULL));                                                          // 随机数种子

    // 设置砖块
    for (int i = 0; i < MaxBrickCount; i++)
    {
        do {
            x = rand() % (Height - 2) + 1;
            y = rand() % (Width - 2) + 1;
        } while ((x == 1 && y == 1) || (x == Height - 2 && y == Width - 2));
  
        Maze[x][y] = 1;
    }

    return Maze;
}

二、小球寻路

/**
 * @brief 使用递归给小球寻路
 * 
 * @param Maze 迷宫
 * @param Width 迷宫的宽
 * @param Height 迷宫的高
 * @param x1 起始点的行坐标
 * @param y1 起始点的列坐标
 * @param x2 终点的行坐标
 * @param y2 终点的列坐标
 * @return int 如果找到通路,返回1,否则返回0
 * 
 * @note
 *      当Maze[x][y]为0时,表示该点还没有走过;
 *      当Maze[x][y]为1时,表示该点为墙;
 *      当Maze[x][y]为3时,表示该点为已经走过,但是走不通;
 *      默认策略为下→右→上→左,如果该点走不通,再回溯
 */
int SeekWay(int ** Maze, int Width, int Height, int x1, int y1, int x2, int y2)
{
    if (Maze[x2][y2] == 2)                                                      // 通路已经找到了
    {
        return 1;
    }
    else
    {
        if (Maze[x1][y1] == 0)                                                  // 该点还没有走过
        {
            // 按默认策略下→右→上→左走
            Maze[x1][y1] = 2;                                                   // 假定该点可以走通
            if (SeekWay(Maze, Width, Height, x1 + 1, y1, x2, y2) == 1)          // 向下走
            {
                return 1;
            }
            else if (SeekWay(Maze, Width, Height, x1, y1 + 1, x2, y2) == 1)    // 向右走
            {
                return 1;
            }
            else if (SeekWay(Maze, Width, Height, x1 - 1, y1, x2, y2) == 1)    // 向上走
            {
                return 1;
            }
            else if (SeekWay(Maze, Width, Height, x1, y1 - 1, x2, y2) == 1)    // 向左走
            {
                return 1;
            }
            else
            {
                Maze[x1][y1] = 3;                                               // 说明该点是死路
                return 0;
            }
        }
        else
        {   
            // 程序指定到这,该点一定走过,之前通过该点的那条路不通,进行递归回溯
            // 如果Maze[x][y]==1,表示该点是墙,不能走
            // 如果Maze[x][y]==3,表示该点是死路
            // 如果Maze[x][y]==2,表示该点是已经走过,进行递归回溯时,说明之前通过该点通路不通
            return 0;
        }
    } 
}

三、打印迷宫

/**
 * @brief 打印迷宫
 * 
 * @param Maze 迷宫
 * @param Width 迷宫的宽
 * @param Height 迷宫的高
 */
void PrintMaze(int ** Maze, int Width, int Height)
{
    for (int i = 0; i < Height; i++)
    {
        for (int j = 0; j < Width; j++)
        {
            printf("%-3d", Maze[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

四、main()函数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int Width = 10;
    int Height = 10;
    int MaxBrickCount = 15;

    int ** Maze = CreateMaze(Width, Height, MaxBrickCount);

    printf("迷宫:\n");
    PrintMaze(Maze, Width, Height);

    SeekWay(Maze, Width, Height, 1, 1, Width - 2, Height - 2);

    printf("小球寻路:\n");
    PrintMaze(Maze, Width, Height);

    return 0;
}
posted @ 2023-06-19 18:10  星光映梦  阅读(157)  评论(0)    收藏  举报