zoukankan      html  css  js  c++  java
  • 【PDD4 迷宫寻路】

    PDD4 迷宫寻路

    来着牛客网的一道拼多多笔试题,有门有锁的一个迷宫问题。

    题目描述

    假设一个探险家被困在了地底的迷宫之中,要从当前位置开始找到一条通往迷宫出口的路径。迷宫可以用一个二维矩阵组成,有的部分是墙,有的部分是路。迷宫之中有的路上还有门,每扇门都在迷宫的某个地方有与之匹配的钥匙,只有先拿到钥匙才能打开门。请设计一个算法,帮助探险家找到脱困的最短路径。如前所述,迷宫是通过一个二维矩阵表示的,每个元素的值的含义如下 0-墙,1-路,2-探险家的起始位置,3-迷宫的出口,大写字母-门,小写字母-对应大写字母所代表的门的钥匙。

    输入描述:
    迷宫的地图,用二维矩阵表示。第一行是表示矩阵的行数和列数M和N 后面的M行是矩阵的数据,每一行对应与矩阵的一行(中间没有空格)。M和N都不超过100, 门不超过10扇。

    输出描述:
    路径的长度,是一个整数

    示例1
    输入:

    5 5
    02111
    01a0A
    01003
    01001
    01111
    

    输出:

    7
    

    解题思路

    普通的迷宫问题比较简单,利用 bfs 只要到达就是最少步数的特点,直接就能解决。这道题的难点在于门的出现,要求必须有钥匙才能通过。而如果要拿到钥匙的话,就不能保证每个点访问一次,也就没法保证走到的所有点都是最短距离……

    这里的话我们用一种形象的描述来解决这个问题:现在要从一楼出发去某个房间,路上有一些门没有钥匙。但是中间有一些楼梯可以让我们上楼,上去之后可以在上面的楼层绕道门后再下来……

    根据这种描述,我们可以得到灵感:原本的迷宫问题是在每个点维护最少步数这一个状态,现在我们在每个点维护多重状态,也就是持有k把钥匙时的最少步数。这样每次经过钥匙更新一下状态,每次到门都去检查是否有钥匙,来确定此时能不能通过。当然,这里用于过滤重复访问的 vis 数组也要增加一个维度。

    相应的,其他类似改进型,也都是通过状态的增加来解决的。

    参考代码

    #include <assert.h>
    #include <string.h>
    #include <iostream>
    #include <queue>
    using namespace std;
    
    const int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
    
    int m, n;
    char ndarr[103][103];
    bool vis[101][101][1<<10];
    int startX, startY;
    
    struct State {
        int x, y;
        int step;
        int keys; // 使用二进制表示持有的钥匙列表
    };
    
    int bfs() {
        assert(startX >= 0 && startY >= 0);
        queue<State> q;
        q.push({startX, startY, 0, 0});
        memset(vis, false, sizeof(vis));
        while (!q.empty()) {
            auto [x, y, step, key] = q.front();
            q.pop();
            if (ndarr[x][y] == '3') {
                return step;
            }
            for (int k=0; k<4; k++) {
                int i = x + dir[k][0];
                int j = y + dir[k][1];
                if (i<0 || i>=m || j<0 || j>=n) continue;
                if (ndarr[i][j] == '3') {
                    return step+1;
                }
                if (ndarr[i][j] == '0') continue;
                if (isupper(ndarr[i][j])
                    && (0 == (key & (1 << (tolower(ndarr[i][j]) - 'a'))))) continue;
                int newkey = key;
                if (islower(ndarr[i][j])) {
                    newkey |= (1 << (ndarr[i][j] - 'a'));
                }
                if (!vis[i][j][newkey]) {
                    vis[i][j][newkey] = true;
                    q.push({i, j, step+1, newkey});
                }
            }
        }
        return -1;
    }
    
    int main() {
        cin >> m >> n;
        startX = -1;
        startY = -1;
        for (int i=0; i<m; i++) {
            cin >> ndarr[i];
            if (startX < 0) {
                for (int j=0; j<n; j++) {
                    if (ndarr[i][j] == '2') {
                        startX = i;
                        startY = j;
                        break;
                    }
                }
            }
        }
        printf("%d
    ", bfs());
        return 0;
    }
    
  • 相关阅读:
    Android 开发学习进程0.19 webview 的使用
    2020年4到6月—7家公司面试总结(3家已拿offer)
    iOS今日头条第3轮面试回忆
    [搬运]Dart之枚举中使用扩展
    Proguard 常用规则
    shiro安全框架
    Android服务的AIDL跨进程(程序)操作
    Android——服务的实例,银行服务
    Android四大组件之服务————服务的生命周期和启动方式
    Android 程序间的广播和Manifest找不到(解决方法)
  • 原文地址:https://www.cnblogs.com/zhcpku/p/15212900.html
Copyright © 2011-2022 走看看