迷宫—BFS 牛客

题目链接:https://ac.nowcoder.com/acm/problem/15136

题目描述 

这是一个关于二维迷宫的题目。我们要从迷宫的起点 'S' 走到终点 'E',每一步我们只能选择上下左右四个方向中的一个前进一格。 'W' 代表墙壁,是不能进入的位置,除了墙壁以外的地方都可以走。迷宫内的 'D' 代表一道上锁的门,只有在持有钥匙的时候才能进入。而 'K' 则代表了钥匙,只要进入这一格,就会自动地拿到钥匙。最后 '.' 则是代表空无一物的地方,欢迎自在的游荡。

本题的迷宫中,起点、终点、门跟钥匙这四个特殊物件,每一个恰好会出现一次。而且,此迷宫的四周 (最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是墙壁。

请问,从起点到终点,最少要走几步呢?

输入描述:
输入的第一行有两个正整数H, W,分别代表迷宫的长跟宽。
接下来的H行代表迷宫,每行有一个长度恰为W的字串,此字串只包含`'S'`, `'E'`, `'W'`, `'D '`, `'K'`, `'.'`这几种字元。
输出描述:
请在一行中输出一个整数代表答案,如果无法从起点走到终点,请输出-1。
示例1

输入

4 12
WWWWWWWWWWWW
WE.W.S..W.KW
W..D..W....W
WWWWWWWWWWWW
输出

20
示例2

输入

6 6
WWWWWW
WEWS.W
W.WK.W
W.WD.W
W.W..W
WWWWWW
输出

-1
备注:
4 ≤ H, W≤ 500
'S', 'E', 'K', 'D'各出现恰好一次
迷宫的四周(最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是 'W'

思路:

因为问的是最短路的长度,所以是bfs题

但是这个题介入了钥匙和门这俩恶心的玩意就比较坑!

你不可能通过计算一次bfs就得到答案,因为bfs时每个点只能走一次,用完这个点就扔掉了,不会走回头路,这就会导致一种情况:如样例一

4 12
WWWWWWWWWWWW
WE.W.S..W.KW
W..D..W....W
WWWWWWWWWWWW

第三个就踩到门D了,这个时候你没有钥匙,就会进不去,最后就不能到终点。而这个地方的正解是你先去K处拿到钥匙,再去开门到终点,你会发现会重复走,所以就不是一个bfs能解决的了的事!

我们知道要想到达终点分两种情况,一是不进门去走,看看能不能走到终点,另一种是走门再到终点。

而走门又分为两种情况,一是钥匙会在路上捡到,不用刻意去捡,另一种是得先多跑几步找钥匙,再去开门。总的来说,走门得分两步,先bfs从起点到钥匙处,再bfs从钥匙到门,这样得到的肯定是最小步数。

所以,综上所述,解决这个问题可以分两种情况

  • 将门的位置当成墙,再用bfs去找
  • 先来一步从起点到钥匙的bfs,再来一步从钥匙到门到bfs,再来一步门到终点的bfs,最后三步相加

假设我们bfs没找到最短路时返回的是无穷大,那么如果情况1返回无穷大并且情况二中至少有一个出现无穷大,就说明到不了终点,返回-1

剩下的情况就输出情况一和情况二的最小值

好看一点的AC码
int h, w, x1, yy1, ans, key,xk, yk ,xd, yd;
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
bool vis[505][505];
char tr[505][505];
struct ran{
    int x, y, step;
};
queue<ran>q;
ran over[5];//用来记录点的位置
bool judge(int x, int y){
    if(x > h || x < 1 || y > w || y < 1)return false;
    else if(vis[x][y])return false;
    else if(tr[x][y] == 'W' || tr[x][y] == 'D')return false;
    else return true;
}

int bfs(int xx, int yy){
    ran now, next;
    now.x = xx;now.y = yy;now.step = 0;
    q.push(now);
    vis[xx][yy] = 1;
    while (!q.empty()) {
        now = q.front();q.pop();
//一定要把结束的情况放在外面判断鸭,我之前做了一个判能否走出迷宫的题把这个放在for外面wa了,放里面ac了,所以我写的时候就放里面了,结果wa了,呜呜,写了好长时间呢,后来换了个方法写(也就是这个代码),发现和第一遍一样还是每个步数有的差1有的不差1 ,就发现原来是这个地方出了问题!淦
        if(now.x == x1 && now.y == yy1){
            return now.step;
        }
        for(int i = 0; i < 4; ++i){
            next.x = now.x + dx[i];next.y = now.y +  dy[i];
            if(!judge(next.x, next.y))continue;

            //cout<<now.x<<' '<<now.y<<" -> "<<next.x<<' '<<next.y<<endl;
            vis[next.x][next.y] = 1;
            next.step = now.step + 1;
            q.push(next);
        }
    }
    return inf;
}

void init(){//初始化
    memset(vis, 0, sizeof(vis));
    while (!q.empty()) {
        q.pop();
    }
}

int main(){
    cin>>h>>w;
    for(int i = 1; i <= h; ++i)
    for(int j = 1; j <= w; ++j){
        scanf(" %c",&tr[i][j]);
        if(tr[i][j] == 'S'){//起点
            over[1].x = i;over[1].y = j;
        }
        if(tr[i][j] == 'K'){//钥匙
            over[2].x = i;over[2].y = j;
        }
        if(tr[i][j] == 'D'){//门
            over[3].x = i;over[3].y = j;
        }
        if(tr[i][j] == 'E'){//终点
            over[4].x = i;over[4].y = j;
        }
    }
    init();//从起点到终点
    x1 = over[4].x;yy1 = over[4].y;
    int a = bfs(over[1].x, over[1].y);
    init();//从起点到钥匙
    x1 = over[2].x;yy1 = over[2].y;
    int b = bfs(over[1].x, over[1].y);
    init();//从钥匙到门
    x1 = over[3].x;yy1 = over[3].y;
    tr[over[3].x][over[3].y] = '.';//注意要及时把门给拆了!
    int c = bfs(over[2].x, over[2].y);
    init();//从门到终点
    x1 = over[4].x; yy1 = over[4].y;
    int d = bfs(over[3].x, over[3].y);
    if(a == inf && (b == inf || c == inf || d == inf))
        cout<<-1<<endl;
    else cout<<min(a, b + c + d)<<endl;
    return 0;
}

  • 26
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值