洛谷 - P1363 幻象迷宫

洛谷 - P1363 幻象迷宫

题目链接

实际难度:\(\color{7FF000}{{提高-}}\)

考察知识点

  • 搜索 - Flood Fill

思路分析

本题就是在普通的Flood Fill上加了一些特殊性质。

拿到这个题,可能会有的想法:将图中边缘上的点与其对应的点相连(例如:\((1,5)和(n,5)\)\((5,1)和(5,m)\)(当且仅当图中边缘上的点与其对应的点均可到达)。(这也是本人最先想到的思路)

但是这样对吗?显然不对。

对于以下的样例:

5 6
######
##S..#
##.#.#
##...#
######

以上面的方式抽象成图就会得到:

pVTWiwT.png

显然应当输出No,但是依据上面的方法会误判成Yes

这是因为:我们只考虑到了环,而没考虑到环的类型(环可以是不跨边界形成的环,也可以是跨边界形成的环)。

所以我们只需维护是否存在一个点,跨过边界能重复到达。

复杂度分析

时间复杂度

\[\begin{aligned} T(n)&=\underbrace{O(n \times m)}_{寻找起点}+\underbrace{O(n \times m)}_{DFS}\\ &=O(n \times m) \end{aligned} \]

空间复杂度

  • 主要存储:\(O(n \times m)\)
  • 临时变量:\(O(n \times m)\)
  • 总空间:\(O(n \times m)\)

C++代码

// Problem: P1363 幻象迷宫
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1363
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <cstdio>   // 用于输入输出操作(如scanf、printf)
#include <cstring>  // 用于内存操作(如memset)
using namespace std;

// 方向数组:表示上下左右四个移动方向(左、上、右、下)
// dx[i]和dy[i]分别对应第i个方向的x和y坐标偏移量
const int dx[] = {0, -1, 0, 1};  // x方向偏移(0:左移不影响x,-1:上移x-1,0:右移不影响x,1:下移x+1)
const int dy[] = {-1, 0, 1, 0};  // y方向偏移(-1:左移y-1,0:上移不影响y,1:右移y+1,0:下移不影响y)

const int N = 1510;  // 迷宫最大尺寸(题目中迷宫大小不超过1500x1500)

// 全局变量定义
int n, m;               // n:迷宫行数,m:迷宫列数
char g[N][N];           // 存储迷宫地图:'.'表示可走,'#'表示障碍物,'S'表示起点
bool flag;              // 标记是否找到符合条件的路径(存在幻象环则为true)
int s[N][N][2];         // 记录到达坐标(x,y)时的"循环次数":
                        // s[x][y][0]为x方向循环次数,s[x][y][1]为y方向循环次数
bool vis[N][N];         // 标记坐标(x,y)是否已被访问过(用于DFS剪枝和环检测)
int sx, sy;             // 起点坐标(sx, sy)


/*
 * DFS搜索函数:从当前坐标(x,y)出发,探索所有可能路径,检测是否存在幻象环
 * x:当前位置的x坐标(迷宫范围内的实际坐标)
 * y:当前位置的y坐标(迷宫范围内的实际坐标)
 * stepx:x方向的循环次数(每从顶部出界一次+1,从底部出界一次-1)
 * stepy:y方向的循环次数(每从右侧出界一次+1,从左侧出界一次-1)
 */
void dfs(int x, int y, int stepx, int stepy) {
    // 处理x方向的循环边界:走出迷宫上边界(x<0),从下边界进入
    if (x < 0) {
        x = n - 1;    // 从底部进入(x坐标变为最后一行)
        stepx--;      // 上移出界,x方向循环次数减1
    }
    // 处理x方向的循环边界:走出迷宫下边界(x>=n),从上边界进入
    if (x >= n) {
        x = 0;        // 从顶部进入(x坐标变为第一行)
        stepx++;      // 下移出界,x方向循环次数加1
    }
    // 处理y方向的循环边界:走出迷宫左边界(y<0),从右边界进入
    if (y < 0) {
        y = m - 1;    // 从右侧进入(y坐标变为最后一列)
        stepy--;      // 左移出界,y方向循环次数减1
    }
    // 处理y方向的循环边界:走出迷宫右边界(y>=m),从左边界进入
    if (y >= m) {
        y = 0;        // 从左侧进入(y坐标变为第一列)
        stepy++;      // 右移出界,y方向循环次数加1
    }

    // 若当前位置是障碍物('#'),无法继续探索,直接返回
    if (g[x][y] == '#') return;

    // 若已找到符合条件的路径(flag为true),提前终止搜索
    if (flag) return;

    // 若当前位置已被访问过(说明形成回路)
    if (vis[x][y]) {
        // 检查:之前到达该位置时的循环次数(stepx, stepy)与本次是否不同
        // 若不同,说明存在"同一位置但循环次数不同"的路径,即存在幻象环
        if (s[x][y][0] != stepx || s[x][y][1] != stepy) {
            flag = true;  // 标记找到有效路径
        }
        return;  // 无论是否符合条件,该路径已探索完毕,返回
    }

    // 标记当前位置为已访问,并记录到达时的循环次数
    vis[x][y] = true;
    s[x][y][0] = stepx;  // 记录x方向循环次数
    s[x][y][1] = stepy;  // 记录y方向循环次数

    // 向四个方向(左、上、右、下)递归探索
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i];  // 新x坐标(未处理边界前)
        int ny = y + dy[i];  // 新y坐标(未处理边界前)
        // 递归探索下一个位置,循环次数暂不改变(边界处理在递归中完成)
        dfs(nx, ny, stepx, stepy);
    }
}

int main() {
    // 多组测试数据处理(当输入n和m成功时继续循环)
    while (scanf("%d%d", &n, &m) != EOF) {
        // 初始化访问标记数组(所有位置均未访问)
        memset(vis, false, sizeof(vis));
        // 初始化循环次数记录数组(默认值为0)
        memset(s, 0, sizeof(s));
        // 初始化标记:尚未找到符合条件的路径
        flag = false;

        // 读取迷宫地图(n行,每行m个字符)
        for (int i = 0; i < n; i++) {
            scanf("%s", g[i]);
        }

        // 寻找起点'S'的位置,并将其改为'.'(起点视为可走路径的一部分)
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (g[i][j] == 'S') {
                    sx = i;    // 记录起点x坐标
                    sy = j;    // 记录起点y坐标
                    g[i][j] = '.';  // 起点改为可走路径,避免后续判断干扰
                }
            }
        }

        // 从起点开始DFS,初始循环次数设为(1,1)(避免0值干扰,实际只要统一即可)
        dfs(sx, sy, 1, 1);

        // 根据flag判断是否存在幻象环,输出结果
        if (flag) puts("Yes");
        else puts("No");
    }
    
    return 0;
}
posted @ 2025-08-19 22:51  九三青梧  阅读(26)  评论(0)    收藏  举报