P1363 幻象迷宫

题目传送门

一、题意分析

题意:给你一个n×m的迷宫g(xy范围是0n10m1),#不能走,.可以走,S作为起点,现在将迷宫扩展成无穷大,扩展方法是:任意一个(x,y)位置的字符c=g(x%n,y%m),现在问你可不可以从起点处走到无穷远处。

举个栗子:

原始迷宫5×5为中间的黄色区域,标红色的位置的坐标为(2,4), 而 (2), (4), 所以(2,4)位置处,应该和g(3,1)相同,所以为S

二、解题思路

对于一个位置A(x,y)如果能够走到另一个位置B(p,q), AB,并满足AB在对应的n×m图中的相对位置相同(意思是:p%n=x%n, q%m=y%m),那么不断重复这个过程,就一定能走到无穷远处。所以考虑从起点(位于原图中,扩展的图中的其他S看成是.)开始dfsbfs(此处是flood fill),一旦两次都遍历到了原图上的同一位置,并且这两次遍历到的点不同,那么说明可以走到无穷远处,否则不能。

注意红色部分:假设两次遍历到的点为xypq, 那么遍历到原图上的同一位置指的是p%n=x%n, q%m=y%m(是就相对位置而言的)

一开始我是每遍历到一个点(x,y)就对原图上的(x%n,y%m)标记访问过,然后在后面的遍历过程中一旦发现一个位置p,qp%n,q%m已经被访问过,那么就认为能够走到无穷远,这是错的,因为有可能(x,y)(p,q)是同一个点。

由于模运算的不可逆,意思就是说,如果只是简单的在访问到xy时,对x%ny%m标记已访问,那么在下一次搜索到某点p,q时就算p%nq%m恰好为(x,y)标记过的地方,也不能肯定(p,q)(x,y)不是同一点,所以有必要对于原图中的每一个位置(u,v)保存一下第一次访问到它(指x%n=u,y%m=v)的坐标(x, y)。

三、bfs常规解法

#include<bits/stdc++.h>

using namespace std;
const int N = 1510;
struct coord {
    int x;
    int y;
};
//路径中的某个点,如果再次被访问到,并且,不是原来的前驱时,就可以交替进行这样的操作,不断外延出去。
//走迷宫下右上左
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

int n, m;
int sx, sy;//出发点坐标

char g[N][N];       //地图
int book[N][N][3];  // book[u][v][0~2]分别表示:是否访问过,x和y

/**
 * 功能:正数+负数取模
 */
int MOD(int a, int b) {
    return (a % b + b) % b;
}

//广度优先搜索
bool bfs() {
    queue<coord> q;           //声明队列
    q.push({sx, sy});       //将出发点放入队列

    //标识起点已使用,并且第一次到达时,真实坐标是x,y
    book[sx][sy][0] = 1, book[sx][sy][1] = sx, book[sx][sy][2] = sy;

    //开始套路
    while (!q.empty()) {
        int x = q.front().x;
        int y = q.front().y;
        q.pop();
        for (int i = 0; i < 4; ++i) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            //因为可能出现负数,使用负数取模运算
            int r = MOD(tx, n), c = MOD(ty, m);
            //如果不是墙
            if (g[r][c] != '#') {
                //如果走过.而且与上次过来的位置不一样的话,就是肯定能走出去
                if (!book[r][c][0]) {
                    //没有走过,入队列
                    q.push({tx, ty});
                    //标识已使用,并且第一次到达的坐标是a,b
                    book[r][c][0] = 1, book[r][c][1] = tx, book[r][c][2] = ty;
                } else if ((book[r][c][1] != tx || book[r][c][2] != ty)) return true;
            }
        }
    }
    return false;
}

int main() {
    while (cin >> n >> m) {
        //每次清空,地图数组不用每次清空,因为每次都全新读入,自
        memset(book, 0, sizeof book);
        //双重循环读入地图
        // 注意i是从0开始,到n-1结束,这是因为:表示迷宫里(0,0)到(n-1,m-1)这个矩阵单元。
        // 注意j是从0开始,到m-1结束,这是因为:表示迷宫里(0,0)到(n-1,m-1)这个矩阵单元。
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                cin >> g[i][j];
                if (g[i][j] == 'S')sx = i, sy = j;     //标识起点
            }
        //广度优先
        if (bfs())cout << "Yes" << endl;    //可以走出幻象迷宫
        else cout << "No" << endl;            //不能走出幻象迷宫
    }
}

四、bfs取hash降维解法

#include<bits/stdc++.h>

using namespace std;
const int N = 1510;
int n, m;
//坐标结构体
struct coord {
    int x, y;
};

//走迷宫下右上左
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

/**
 * 功能:正数+负数取模
 */
int MOD(int a, int b) {
    return (a % b + b) % b;
}

int book[N][N];
char g[N][N];


//随便写的hash函数
int Hash(int x, int y) {
    return x * 10000 + y;//因为x,y的极值是1500,就是4位,如果x*10000,最小是1万以上,再加上y,y就在后4位
    //也可以少乘点
}

/**
 * 功能:广度优先搜索
 * @param x  坐标x
 * @param y  坐标y
 * @return  是否能走出迷宫
 */
bool bfs(int x, int y) {
    queue<coord> q;
    //入队列
    q.push((coord) {x, y});

    book[x][y] = Hash(x, y);
    while (!q.empty()) {
        coord u = q.front(), v;
        q.pop();

        for (int i = 0; i < 4; i++) {
            v.x = u.x + dx[i];
            v.y = u.y + dy[i];
            int r = MOD(v.x, n);//下一步的相对位置x
            int c = MOD(v.y, m);//下一步的相对位置y
            int h = Hash(v.x, v.y);
            //如果下一个位置不是障碍物
            if (g[r][c] != '#') {
                //如果下一个位置没有走过
                if (!book[r][c]) {
                    book[r][c] = h;//记录本次hash值
                    q.push(v);//入队列
                } else if (book[r][c] != h) return true;//上次的hash值与本次的不一样,说明来自不同的前驱,就表示可以走出去
            }
        }
    }
    return false;
}

int main() {
    while (cin >> n >> m) {
        //清空状态数组
        memset(book, 0, sizeof(book));
        //读入
        int x, y;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                cin >> g[i][j];
                if (g[i][j] == 'S')x = i, y = j;     //标识起点
            }
        //广度优先搜索
        if (bfs(x, y)) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

五、dfs常规解法

#include <bits/stdc++.h>

using namespace std;
const int N = 1510;//题目要求是1500
//https://www.cnblogs.com/tomori/p/14320956.html
int n, m;

char g[N][N];    //邻接矩阵,也就是二维数据保存地图
int st[N][N][3]; // st[u][v][0~2]分别表示:是否访问过,x和y

//走迷宫下右上左
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

/**
 * 功能:正数+负数取模
 */
int MOD(int a, int b) {
    return (a % b + b) % b;
}

bool success;
/**
 * 功能:在邻接矩阵上面的深度优先搜索,从x,y这个点出发,是不是能走出幻象迷宫
 * @param x
 * @param y
 * @return
 */
void dfs(int x, int y) {
    for (int i = 0; i < 4; i++) {           //遍历四个方向
        int tx = x + dx[i], ty = y + dy[i];   //得到下一步的坐标
        //因为可能出现负数,使用负数取模运算
        int r = MOD(tx, n), c = MOD(ty, m);
        //有的地方是墙,用'#'表示
        if (g[r][c] != '#') {
            if (st[r][c][0]) {                //如果此位置走过
                if (st[r][c][1] != tx || st[r][c][2] != ty) {
                    success = true;           //并且不是从当前的位置过来的,就是有其它一条道路走过来的,表示可以走通
                    return;
                }
            } else {
                //第一次访问到它(指x % n = u, y % m = v)的坐标(x, y)。
                st[r][c][0] = 1, st[r][c][1] = tx, st[r][c][2] = ty;
                //开始递归下一个位置
                dfs(tx, ty);
            }
        }
    }
}

int main() {
    //循环接收多次,ctrl+d结束
    while (cin >> n >> m) {
        //清空状态数组
        memset(st, 0, sizeof st);
        success = false;
        int sx, sy;//出发点
        //双重循环读入地图
        // 注意i是从0开始,到n-1结束,这是因为:表示迷宫里(0,0)到(n-1,m-1)这个矩阵单元。
        // 注意j是从0开始,到m-1结束,这是因为:表示迷宫里(0,0)到(n-1,m-1)这个矩阵单元。
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) {
                cin >> g[i][j];
                //记录出发点
                if (g[i][j] == 'S') sx = i, sy = j;
            }

        //第一次访问到它(指x % n = u, y % m = v)的坐标(x, y)。
        st[sx][sy][0] = 1, st[sx][sy][1] = sx, st[sx][sy][2] = sy;
        //深度优先搜索
        dfs(sx, sy);

        if (success)cout << "Yes" << endl; //可以走出幻象迷宫
        else cout << "No" << endl;            //不能走出幻象迷宫
    }
    return 0;
}
posted @   糖豆爸爸  阅读(179)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2019-08-17 基于JFinal中搭建wopi协议支撑办法
2014-08-17 转发给吴斌
Live2D
点击右上角即可分享
微信分享提示