18276 走迷宫

Description
有一个N*M的格子迷宫,1代表该格子为墙,不能通过,0代表可以通过,另外,在迷宫中
有一些传送门,走到传送门的入口即会自动被传送到传送门的出口(一次传送算1步)。人在迷宫中可以尝试
上下左右四个方向移动。现在给定一个迷宫和所有传送门的出入口,以及起点和终点,
问最少多少步可以走出迷宫。如果不能走出迷宫输出“die”。

输入格式
该程序为多CASE,第1行为CASE的数量
每一个CASE,第1行为两个数N(行)和M(列)
然后N行每行M个数
之后是一个数W,为传送门的数量
之后每行一个传送门的入口坐标c1(行),r1(列)和出口坐标c2,r2
之后是起点坐标和终点坐标sc(行) sr(列) ec(行) er(列)

注:传送门出入口和起点坐标和终点坐标不会出现在墙的位置
所有数字不超过100

输出格式
如题

输入样例
2
4 3
011
011
110
110
1
1 0 2 2
0 0 3 2
2 2
01
10
0
0 0 1 1

输出样例
3
die

一、深度搜索DFS->由于数据量较大所以超时

//超时:深度搜索
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int N, M, W, result = 0;
char map[200][200] = { 0 };
int v[200][200] = { 0 }, dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
int sc, sr, ec, er;
struct door
{
    int c1;
    int r1;
    int c2;
    int r2;
} door[100];
int min_step = 1e9;

void dfs(int x, int y, int step)
{
    //检查当前位置是否是传送点入口
    int j;
    for (j = 0; j < W; j++)
    {
        if (x == door[j].c1 && y == door[j].r1)
        {
            if (v[door[j].c2][door[j].r2] == 1)
                return;
            x = door[j].c2;
            y = door[j].r2;
            step++;
            v[x][y] = 1;
            break;
        }
    }
    if (x == ec && y == er)
    {
        min_step = min_step < step ? min_step : step;
        result = 1;
        return;
    }

    int i;
    for (i = 0; i < 4; i++)
    {
        int cur_x = x + dx[i], cur_y = y + dy[i];
        if (cur_x >= 0 && cur_x < N && cur_y >= 0 && cur_y < M && map[cur_x][cur_y] == '0' && v[cur_x][cur_y] == 0)
        {
            v[cur_x][cur_y] = 1;

            dfs(cur_x, cur_y, step + 1);
            v[cur_x][cur_y] = 0;
        }
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        result = 0;
        min_step = 1e9;
        memset(v, 0, sizeof(v));
        memset(door, 0, sizeof(door));
        int i, j;
        scanf("%d%d", &N, &M); getchar();
        for (i = 0; i < N; i++)
        {
            for (j = 0; j < M; j++)
                scanf("%c", &map[i][j]);
            getchar();
        }
        cin >> W;
        for (i = 0; i < W; i++)
            cin >> door[i].c1 >> door[i].r1 >> door[i].c2 >> door[i].r2;
        cin >> sc >> sr >> ec >> er;
        v[sc][sr] = 1;
        dfs(sc, sr, 0);
        if (result == 0)
            cout << "die" << endl;
        else
            cout << min_step << endl;
    }

    return 0;
}

二、广度搜索BFS->AC

/*
思路解析:
广度搜索用的数据结构为队列,如果是普通的迷宫问题,其实有个特点,就是从每一步延伸出来下一步的坐标,都是当前步数加一, 所以可以统一的将每一步所延伸出来的步数一起出队,len = r - f; len即为上一步延伸出来的可走的位置数,len个位置全部出队,则步数step++; 但是这个本质是一个一个位置出队,下一步的步数是当前步的步数加一,即可以用结构体Pos封装一下x、y坐标外,再加入一个记录当前步数的变量,以便用队列统计每一步的步数,即q[r].step = q[f].step+1,理解为 由当前位置延伸的位置的步数 为 当前位置的步数 加一,借此可以统计走到地图中每一个位置的步数,又因为是广度搜索,所以到达终点的一定是最短路径,所以直接判断队头是否是终点即可。其他的判断则是判断当前位置是否是传送门,如果是,则判断传送点的出口有没有走过,如果走过,就不必继续再走了,如果没有走过,那么就可以就将出口坐标入队,并且标记走过。这样不用for循环统一将某一步的位置出队,即可将传送点的情况跟普通位置的情况出队入队统一起来,计算最短步数
*/

//广搜AC
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int N, M, W, result = 0;
char map[200][200] = { 0 };
int v[200][200] = { 0 }, isdoor[200][200] = { 0 }, dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
int sc, sr, ec, er;//入口、出口行列数


//传送点入口出口结构体
struct door
{
    int c1;
    int r1;
    int c2;
    int r2;
} door[105];

//记录结构体
typedef struct pos
{
    int x;//记录当前点的行数
    int y;//记录当前点的列数
    int step;//记录步数
} Pos;

//广搜bfs
int bfs()
{
    //创造静态队列,记录位置和到达当前位置的步数
    Pos q[100000] = { 0 };//注意队列数组要开大一点,否则会报错
    int f, r;
    f = 0, r = 0;
    q[r].x = sc, q[r].y = sr,q[r].step = 0;
    r++;
    while (f != r)
    {
        //判断当前点是否是终点,如果是终点,则返回步数
        if (q[f].x == ec && q[f].y == er)
            return q[f].step;
        //如果当前点为传送点
        if (isdoor[q[f].x][q[f].y] != 0)
        {
            //记录数组存储传送门的下标方便访问
            int out_index = isdoor[q[f].x][q[f].y];
            int out_x = door[out_index].c2;
            int out_y = door[out_index].r2;
            //如果出口是墙 或者 不在地图内 或者 已经被走过,则将队头出队
            if (map[out_x][out_y] == '1' || out_x < 0 || out_x >= N || out_y < 0 || out_y >= M || v[out_x][out_y] == 1)
            {
                f++;
                continue;
            }
            //把传送门的出口坐标入队
            q[r].x = out_x;
            q[r].y = out_y;
            q[r].step = q[f].step + 1;
            r++;
            v[out_x][out_y] = 1;//标记传送门出口走过
        }
        else //如果当前点不是出口也不是传送点,则往四个方向走,并且将可以走的点入队
        {
            int i;
            for (i = 0; i < 4; i++)
            {
                int cur_x = q[f].x + dx[i], cur_y = q[f].y + dy[i];
                if (cur_x >= 0 && cur_x < N && cur_y >= 0 && cur_y < M && map[cur_x][cur_y] == '0' && v[cur_x][cur_y] == 0)
                {
                    v[cur_x][cur_y] = 1;//标记当前位置走过
                    q[r].x = cur_x;
                    q[r].y = cur_y;
                    q[r].step = q[f].step + 1;
                    r++;
                }
            }
        }
        f++;//队头出队
    }
    return -1;
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        memset(v, 0, sizeof(v));
        memset(isdoor, 0, sizeof(isdoor));
        memset(door, 0, sizeof(door));
        //读入行和列
        scanf("%d%d", &N, &M);
        getchar();
        //读入路和墙,即地图
        int i, j;
        for (i = 0; i < N; i++)
        {
            for (j = 0; j < M; j++)
                scanf("%c", &map[i][j]);
            getchar();
        }
        //读入传送门个数
        scanf("%d",&W);
        //读入传送门入口和出口坐标,同时标记传送门的位置
        for (i = 1; i <= W; i++)
        {
            cin >> door[i].c1 >> door[i].r1 >> door[i].c2 >> door[i].r2;
            isdoor[door[i].c1][door[i].r1] = i;//标记为是传送点
        }
        //读入入口和出口坐标
        cin >> sc >> sr >> ec >> er;
        //标记入口已经走过
        v[sc][sr] = 1;
        //广度搜索
        int step = bfs();
        if (step == -1)
            printf("die\n");
        else
            printf("%d\n", step);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值