密室逃脱——广度优先搜索算法

密室逃脱

小丫喜欢玩密室逃脱,每次游戏开始时,小丫会进入一个密室,她需要按照顺序解开各个隐藏线索才能成功逃脱密室。

小Y非常聪明,解开线索对她来说并不难,但是她有一点懒,她希望在通关过程中移动次数最少。请你帮小丫计算她至少要移动多少次才能成 功通关。

密室是m 行n列的格子矩阵,小Y从左上角(1,1)进入密室,密室中有三种格子: 墙,以数字0标记 路,以数字1标记 隐藏线索处,以数字(> 1)标记,代表该线索的难度 小Y需要按照难度递增的顺序解开各个线索,逃脱密室。

时间限制:1000

内存限制:65536

输入

第一行是一个整数 T,表示输入包含 T 组数据,分别是不同的游戏中小Y所处的密室。

对于每组数据,第一行包括两个整数:m(1<= m <= 100)n(1 <= n <= 100)。

接下来 m 行,每行有n个数字,第ì行的第j个数字表示密室中第ì行第 j列的格子的类型。

题目保证进入密室处(1,1)不是墙壁,线索的难度都不相同。

输出

对于每组数据,你需要输出一个整数,表示小Y在这个密室中至少要移动多少次才能成功通关。如果小Y不可能解开所有线索,输出-1.

样例输入

2

3 3

1 3 2

1 0 4

10 6 5

3 3

1 3 2

0 0 0

10 6 5

样例输出

8

-1

提示

样例解释:由于需要按难度顺序解开线索,在第一组数据中,小丫第一次移动到 3 时不能解密,在完成2之后需要回到 3.最后小Y解开 10 时,她成功通关。

思路讲解:

该题与一般的广搜算法题不同,每一个密室是可以重复走的,这样会让我们的算法不知道在哪里停下来。

但仔细思考后发现,其实在搜索相同难度的密室时,是不用重复走的。具体原因如下:

1.广搜算法是一层一层展开,因此第一次到达一个密室时,所用步数就是最小的那次;

2.只需要对所有密室搜索一遍就可以找到最短路径下的密室,或者找不到就返回-1;

解题:

//示例代码 条件+广搜
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int m,n;
int maze[104][104] = {};
int dx[4] = {0,0,-1,1};
int dy[4] = {-1,1,0,0};

// 判断x y 坐标是否越界
bool legal(int x, int y){
    if(0 <= x && x< m && 0 <= y && y < n && maze[x][y] != 0)return 1;
    return 0;
}
// 结构体,记录坐标
struct Node{
    int x, y, k, t;
    // x:行坐标,y:列坐标,k:该密室被寻找第k个难度的搜索访问过,在搜索更高难度的密室时,可以路过被低难度搜索过的密室;t:走到该密室最小步数
};
int main()
{
    int T;
    cin >> T;
    while(T--){
        memset(maze, 0, sizeof(maze));
        int vis[104][104] = {};//记录该密室被访问次数
        memset(vis, 0xff, sizeof(vis));// 把vis数组值全部设置为-1
        int nanduS[10050] = {};// 所有线索的难度都存在该数组,以便于从小到大搜索。
        int cnt = 0;
        cin >> m >> n;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                cin >> maze[i][j];
                if (maze[i][j] > 1) {
                    nanduS[cnt++] = maze[i][j];
                }
            }
        }
        queue<Node> open;
        sort(nanduS, nanduS + cnt); // 对难度数据进行从小到大排序
        // 起始点如果是要解开线索的第一个密室,设置k=1,否则设置k=0;(k代表k难度有没有在该密室被搜索过)
        if (maze[0][0] == nanduS[0]) {
            open.push({0, 0, 1, 0});
            vis[0][0] = 1;
        }
        else {
            open.push({0, 0, 0, 0});
            vis[0][0] = 0;
        }
        bool flag = 0;
       
        while (!open.empty()) {
            Node temp = open.front();
            open.pop();
            if (temp.k == cnt) {//难度序号等于密室总数,表示所有密室都已经被找到。
                flag = 1;
                cout << temp.t << endl;
                break;
            }
            for (int i = 0; i < 4; i++) {//寻找上下左右四个方向的密室
                int x = temp.x + dx[i], y = temp.y + dy[i], k = temp.k;
                if (legal(x,y)) { // 如果超出数组边界,直接跳过,继续下次循环。
                    if (maze[x][y] == nanduS[k]) k++;//找到当前难度的密室,难度k+1,继续找下一个难度的密室
                    // 当前正在搜索的密室难度大于当前密室难度,表示在当前难度还没有访问过该密室,可以借道搜索。
                    if (vis[x][y] < k){
                        vis[x][y] = k;//
                        open.push({x, y, k, temp.t + 1});
                    }
                }
            }
        }
        if (!flag) cout << -1 << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诗九趁年华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值