简单搜索——Dungeon Master

简单搜索

题目

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides.

Is an escape possible? If yes, how long will it take?

输入

The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size).
L is the number of levels making up the dungeon.
R and C are the number of rows and columns making up the plan of each level.
Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a ‘#’ and empty cells are represented by a ‘.’. Your starting position is indicated by ‘S’ and the exit by the letter ‘E’. There’s a single blank line after each level. Input is terminated by three zeroes for L, R and C.

输出

Each maze generates one line of output. If it is possible to reach the exit, print a line of the form
Escaped in x minute(s).

where x is replaced by the shortest time it takes to escape.
If it is not possible to escape, print the line
Trapped!

示例

  • 输入
    3 4 5
    S…
    .###.
    .##…
    ###.#

##.##
##…

#.###
####E

1 3 3
S##
#E#

0 0 0

  • 输出
    Escaped in 11 minute(s).
    Trapped!

思路

我在第一次做此题时,因为前一题刚做过一个深搜的题目,所以惯性思维去尝试用深度优先搜索的思想解决该题,但是写着写着发现思路是不对的。因为如果用深搜的思想做的话,以起始点开始,对可移动的 6 个方向(上一层、下一层 上一行、下一行 前一列、后一列)沿着其中一个方向,需要不停地往后走,沿着某一条路走到终点后,却不能保证该条路就是最短的,可能剩下的遍历中会存在某一条路更短。因此如果用深搜的思想,需要把每一条路都遍历一遍,从中选出最短的作为答案,但是这样的话很有可能会超时,毕竟遍历的太多了。
所以该题的最佳解法是广度优先搜索。广搜的思想用在此题是由起始点开始向 6 个方向(上一层、下一层 上一行、下一行 前一列、后一列)扩散,将其中能移动到的位置记录下来,由这些位置继续进行 6 个方向的扩散,因此在搜索的过程中,6 个方向上的点都是处于同一层,即移动到他们位置的步数是相同的,直到某一个方向上的点进行扩散时直接走到了终点 ‘E’,则直接可以输出此时的步数,因为这条路是最短到达终点的,其他路不可能比他更短。

在使用广度优先搜索的时候可以多尝试使用队列、结构体等。

深度优先搜索和广度优先搜索的总结可以参照我之前的博客:简单搜索(广度优先搜索、深度优先搜索)总结

代码

#include <iostream>
#include <queue>
#include <memory.h>

using namespace std;

// 定义输入的 层-l 行-r 列-c
int l, r, c;
// 定义输入的迷宫
char maze[35][35][35];

// 定义对于迷宫中每一个位置有可能走的方向:
// 上一层、下一层  上一行、下一行  前一列、后一列
int next_step[6][3] =
{
    { -1, 0, 0 }, { 1, 0, 0 },
    { 0, -1, 0 }, { 0, 1, 0 },
    { 0, 0, -1 }, { 0, 0, 1 }
};

// 定义一个用于表示在迷宫中移动时的点的信息,
// 包括 该点的位置:(z, x, y) 和移动到该点时已经移动了多少步:step
struct coordinate
{
    int z, x, y;
    int step;

    coordinate(int in_z, int in_x, int in_y, int in_step)
    {
        z = in_z;
        x = in_x;
        y = in_y;
        step = in_step;
    }
};

// 主要实现函数,广度优先搜索
// 输入 z、x、y 表示起始点的位置
int bfs(int z, int x, int y)
{
    // 定义一个队列,用于存储位置点,存储于其中的位置都是需要以该位置进行后续移动的
    queue<coordinate>q;

    // 将起始点信息存储到结构体对象 current 中
    coordinate current(z, x, y, 0);
    // 将起始点添加到队列中,此时队列不为空,且需要进行移动的点就是起始点
    q.push(current);

    int step;
    // 当队列不为空时,即队列中存在需要进行移动的点时,便需要不断循环去移动点
    while (!q.empty())
    {
        // 将队列中的第一个元素(当前需要进行后续移动的点)赋予 z、x、y、step 变量中
        z = q.front().z;
        x = q.front().x;
        y = q.front().y;
        step = q.front().step;

        // 由于当前点已经走到,避免后续移动时再走回当前已经走过的点
        // 所以将该点处的迷宫地图属性改为 rock,表示不能再移动到该点
        maze[z][x][y] = '#';

        // 尝试进行 上一层、下一层  上一行、下一行  前一列、后一列 6个方向的移动
        for (int s = 0; s < 6; s++)
        {
            // 如果某一移动方向上不超过迷宫尺寸,则表示有可能移动到该点,继而进行后续判断
            if (((z + next_step[s][0]) >= 0) && ((z + next_step[s][0]) < l) && ((x + next_step[s][1]) >= 0) && ((x + next_step[s][1]) < r) && ((y + next_step[s][2]) >= 0) && ((y + next_step[s][2]) < c))
            {
                // 将下一点的信息存储到对象 next 中
                coordinate next(z + next_step[s][0], x + next_step[s][1], y + next_step[s][2], step + 1);

                // 如果下一点是 'E',则找到出口,并能够保证是最短路径,直接可以结束该函数
                if (maze[next.z][next.x][next.y] == 'E')
                {
                    return next.step;
                }

                // 如果下一点是 '.',表示可以移动到下一点,则将下一点添加到 q 队列中
                // 以便后续以该点为始开启后续的移动尝试
                if (maze[next.z][next.x][next.y] == '.')
                {
                    q.push(next);
                    // 并需要将该点的迷宫地图属性改为 rock,避免后续点的移动再移动到该点
                    maze[next.z][next.x][next.y] = '#';
                }
            }
        }

        // 当上面 6 个方向的尝试移动完成后,当前处理的队列第一个元素的点已经处理完毕,
        // 并且该点周围所有可移动点均添加到队列中,
        // 同时该点周围所有点都是 '#'
        // 因此该点不需要进行后续移动尝试操作,遂将该点从队列中踢出
        q.pop();
    }

    // 如果队列中所有元素都进行了移动尝试后仍未走到终点,则直接返回 0
    return 0;
}

int main()
{
    while (1)
    {
        cin>>l>>r>>c;
        if (l == 0 && r == 0 && c == 0)
            break;

        // 每换一次场景都要初始化迷宫
        memset(maze, '#', sizeof(maze));

        int x, y, z;
        for (int i=0;i<l;i++)
            for (int j=0;j<r;j++)
                for (int k=0;k<c;k++)
                {
                    cin>>maze[i][j][k];
                    // 记录起始点的位置
                    if (maze[i][j][k]=='S')
                    {
                        z=i, x=j, y=k;
                    }
                }

        int shortest_time = 0;

        // 以起始点为开始进行广度优先搜索,找最短路径
        shortest_time = bfs(z, x, y);

        if (shortest_time!=0)
            cout<<"Escaped in "<<shortest_time<<" minute(s)."<<endl;
        else
            cout<<"Trapped!"<<endl;
    }
    return 1;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值