当前位置: 首页 > news >正文

神秘迷宫探险 - 详细题解教程

神秘迷宫探险 - 详细题解教程

一、问题描述

K小姐发现了一座神秘迷宫,需要从入口到达宝藏出口。迷宫具有以下特点:

  • 基本元素:

    • 0: 可通行的道路
    • 1: 不可穿越的墙壁
    • S: 入口
    • E: 宝藏出口
    • 2: 传送门(成对出现)
  • 移动规则:

    • 只能向上、下、左、右四个方向移动
    • 不能走出边界或穿越墙壁
    • 传送门可瞬间传送到配对的另一个传送门,不消耗步数
  • 目标: 求从入口到出口的最短路径步数,无法到达则输出 -1

二、算法思路

2.1 核心算法: BFS(广度优先搜索)

为什么选择BFS?

  • BFS天然保证第一次到达目标点时就是最短路径
  • 适合在无权图或边权相同的图中求最短路径
  • 时间复杂度O(m×n),适合题目数据范围

2.2 传送门处理机制

传送门是本题的核心难点:

  1. 配对规则: 传送门按顺序两两配对

    • 第1个和第2个配对
    • 第3个和第4个配对
    • 以此类推
  2. 传送特性:

    • 传送不消耗步数
    • 从传送门A到传送门B,步数保持不变
  3. 实现方法: 使用哈希表建立传送门之间的双向映射

三、算法步骤详解

步骤1: 地图解析

读取迷宫 → 找到S位置 → 找到E位置 → 收集所有2的位置

步骤2: 传送门配对

遍历传送门数组,每两个建立双向映射关系

步骤3: BFS搜索

初始化队列,加入起点(x, y, steps=0)
↓
取出队首元素
↓
检查是否到达终点 → 是: 返回步数
↓                   否: 继续
尝试4个方向移动
↓
如果当前位置是传送门,尝试传送
↓
将新位置加入队列
↓
重复直到队列为空

四、完整代码实现

#include <bits/stdc++.h>  // 万能头文件,包含所有标准库(STL容器、算法、IO等)
using namespace std;       // 使用标准命名空间,避免写std::前缀int main() {// 关闭C++流与C标准流的同步,提升输入输出速度ios::sync_with_stdio(false);// 解除cin与cout的绑定,进一步加速输入(但不能混用scanf/printf)cin.tie(nullptr);// 声明迷宫的行数m和列数nint m, n;// 从标准输入读取迷宫尺寸cin >> m >> n;// ==================== 第一步: 读取迷宫地图 ====================// 创建一个大小为m的字符串向量,用于存储迷宫的每一行vector<string> maze(m);// 循环读取m行迷宫数据for (int i = 0; i < m; i++) {// 读取第i行的字符串(包含n个字符: 0,1,2,S,E)cin >> maze[i];}// ==================== 第二步: 定位关键位置 ====================// 声明起点和终点的坐标,用pair<行,列>表示pair<int, int> start, end;// 创建向量存储所有传送门的坐标vector<pair<int, int>> portals;// 双层循环遍历整个迷宫,找出S、E和所有2的位置for (int i = 0; i < m; i++) {           // 遍历每一行for (int j = 0; j < n; j++) {       // 遍历每一列// 如果当前位置是起点'S'if (maze[i][j] == 'S') {// 记录起点坐标为(i, j)start = {i, j};} // 如果当前位置是终点'E'else if (maze[i][j] == 'E') {// 记录终点坐标为(i, j)end = {i, j};} // 如果当前位置是传送门'2'else if (maze[i][j] == '2') {// 将传送门坐标添加到列表中portals.push_back({i, j});}}}// ==================== 第三步: 建立传送门配对关系 ====================// 创建映射表: key是一个传送门坐标,value是它配对的另一个传送门坐标map<pair<int, int>, pair<int, int>> portalMap;// 每次步进2,将传送门两两配对 (第0和第1配对,第2和第3配对...)for (int k = 0; k + 1 < portals.size(); k += 2) {// 建立第k个传送门到第k+1个传送门的映射 (A → B)portalMap[portals[k]] = portals[k + 1];// 建立第k+1个传送门到第k个传送门的映射 (B → A) 双向映射portalMap[portals[k + 1]] = portals[k];}// ==================== 第四步: BFS广度优先搜索 ====================// 创建BFS队列,每个元素是array<int,3>存储 {x坐标, y坐标, 当前步数}queue<array<int, 3>> q;// 创建访问标记集合,记录已经访问过的坐标,防止重复访问set<pair<int, int>> visited;// 将起点加入队列,初始步数为0q.push({start.first, start.second, 0});// 标记起点已访问visited.insert(start);// 定义四个方向的偏移量: 上(-1,0) 下(1,0) 左(0,-1) 右(0,1)int dx[] = {-1, 1, 0, 0};   // x方向(行)的偏移int dy[] = {0, 0, -1, 1};   // y方向(列)的偏移// BFS主循环: 当队列不为空时持续搜索while (!q.empty()) {// 取出队首元素,使用结构化绑定自动解包为x, y, stepsauto [x, y, steps] = q.front();// 弹出队首元素q.pop();// -------------------- 检查是否到达终点 --------------------// 如果当前坐标等于终点坐标if (make_pair(x, y) == end) {// 输出最短步数并结束程序cout << steps << "\n";return 0;}// -------------------- 尝试向四个方向移动 --------------------// 遍历四个方向(上、下、左、右)for (int d = 0; d < 4; d++) {// 计算新位置的x坐标 (当前x + 方向偏移)int nx = x + dx[d];// 计算新位置的y坐标 (当前y + 方向偏移)int ny = y + dy[d];// 检查新位置是否合法:// 1. nx >= 0 && nx < m: x坐标在有效范围内// 2. ny >= 0 && ny < n: y坐标在有效范围内// 3. maze[nx][ny] != '1': 不是墙壁// 4. visited.find({nx, ny}) == visited.end(): 未被访问过if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] != '1' && visited.find({nx, ny}) == visited.end()) {// 标记新位置已访问 (必须在入队前标记,防止重复入队)visited.insert({nx, ny});// 将新位置加入队列,步数+1 (普通移动消耗1步)q.push({nx, ny, steps + 1});}}// -------------------- 处理传送门传送 --------------------// 将当前坐标构造为pair用于查询mapauto currentPos = make_pair(x, y);// 检查当前位置是否是传送门 (在portalMap中能找到对应项)if (portalMap.count(currentPos)) {// 获取配对传送门的坐标,使用结构化绑定解包为tx, tyauto [tx, ty] = portalMap[currentPos];// 检查传送目标位置是否未被访问过if (visited.find({tx, ty}) == visited.end()) {// 标记传送目标位置已访问visited.insert({tx, ty});// 将传送目标位置加入队列,步数不变! (传送不消耗步数)q.push({tx, ty, steps});}}}// ==================== 第五步: 无法到达终点 ====================// 如果BFS结束后仍未找到终点,说明无路径可达cout << -1 << "\n";return 0;  // 程序正常结束
}

五、代码详解

5.1 数据结构选择

数据结构用途原因
vector<string>存储迷宫方便按行读取和索引
pair<int,int>存储坐标轻量级坐标表示
vector<pair<int,int>>存储传送门列表动态收集所有传送门
map<pair,pair>传送门映射快速查找配对传送门
queue<array<int,3>>BFS队列存储{x,y,步数}
set<pair<int,int>>访问标记防止重复访问

5.2 关键代码解析

1. 传送门配对

for (int k = 0; k + 1 < portals.size(); k += 2) {portalMap[portals[k]] = portals[k + 1];      // A → BportalMap[portals[k + 1]] = portals[k];      // B → A
}
  • 每次跳过2个建立双向映射
  • 确保从任一传送门都能找到配对门

2. BFS状态管理

auto [x, y, steps] = q.front();  // C++17结构化绑定
  • 优雅地解包队列元素

3. 边界和有效性检查

if (nx >= 0 && nx < m && ny >= 0 && ny < n &&  // 边界maze[nx][ny] != '1' &&                      // 非墙壁visited.find({nx, ny}) == visited.end())    // 未访问

4. 传送门传送逻辑

q.push({tx, ty, steps});  // 注意:steps不加1!

六、样例分析

样例1分析

输入:
5 5
S0000
11110
01010
01010
0000E迷宫可视化:
S 0 0 0 0
1 1 1 1 0
0 1 0 1 0
0 1 0 1 0
0 0 0 0 E路径: S → 右4步到(0,4) → 下4步到(4,4)=E
总步数: 8

样例2分析

输入:
3 3
S00
111
E00迷宫可视化:
S 0 0
1 1 1
E 0 0分析: S和E被1完全隔开,无路径
输出: -1

七、复杂度分析

时间复杂度: O(m×n)

  • 每个格子最多被访问一次
  • 传送门查找O(1)
  • 总操作次数: O(m×n)

空间复杂度: O(m×n)

  • 迷宫存储: O(m×n)
  • 访问集合: O(m×n)
  • 队列最大: O(m×n)
  • 传送门映射: O(传送门数量)

八、注意事项与易错点

⚠️ 易错点1: 传送门步数处理

// ❌ 错误: 传送也加步数
q.push({tx, ty, steps + 1});// ✅ 正确: 传送不加步数
q.push({tx, ty, steps});

⚠️ 易错点2: 传送门配对

// ❌ 错误: 只建立单向映射
portalMap[portals[k]] = portals[k + 1];// ✅ 正确: 建立双向映射
portalMap[portals[k]] = portals[k + 1];
portalMap[portals[k + 1]] = portals[k];

⚠️ 易错点3: 访问标记时机

// ❌ 错误: 出队时标记(可能重复入队)
if (visited.find({nx, ny}) == visited.end()) {q.push({nx, ny, steps + 1});visited.insert({nx, ny});  // 太晚了!
}// ✅ 正确: 入队前立即标记
visited.insert({nx, ny});
q.push({nx, ny, steps + 1});

九、扩展思考

扩展1: 如果传送消耗步数怎么办?

修改传送逻辑:

q.push({tx, ty, steps + 传送消耗});

扩展2: 如果有多个终点?

set<pair<int, int>> endpoints;  // 存储所有终点
// BFS时检查是否到达任一终点
if (endpoints.count({x, y})) { ... }

扩展3: 如果要输出路径?

使用parent数组记录路径:

map<pair<int,int>, pair<int,int>> parent;
// 找到终点后回溯构建路径

十、总结

本题是BFS最短路径的经典应用,关键点:

BFS保证最短路径
传送门不消耗步数
双向映射处理配对关系
visited集合防止重复访问

掌握这道题,你就掌握了:

  • BFS框架应用
  • 特殊移动规则处理
  • 图论最短路径思想
http://www.dtcms.com/a/453832.html

相关文章:

  • VOCO摘要
  • 轻量级个人建站
  • DevDay 2025 开发者大会看点
  • 什么网站可以免费做视频软件做网站导航按钮怎么做
  • 网站建设的基本要素有专门做任务的网站
  • 十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解
  • WDF驱动开发-PNP和电源管理
  • 网站的标题标签一般是写在网站 未备案 支付宝
  • 募投绘蓝图-昂瑞微的成长密码与未来布局
  • 公司网络营销的方案思路seo整站优化外包服务
  • 工程建设公司网站怎么查询网站的域名备案
  • TypeScript 对比 JavaScript
  • 焦作网站设计公司自己怎么做外贸网站
  • ros2 消息订阅与发布示例 c++
  • 廊坊网站建设廊坊企业文化建设网站
  • 纸做的花朵成品网站个人简介html代码模板
  • 【精品资料鉴赏】证券数据治理项目投标技术方案
  • AI大模型核心概念
  • 企业网站模板seo公益建设网站的作用
  • 阿里巴巴1688怎么做网站自建网站阿里云备案通过后怎么做
  • 成都规划网站佛山网上办事大厅官网
  • AI-RAN Sionna 5G NR 开发者套件
  • 百度商桥怎么添加到网站山东网站
  • iis 里没有网站柯桥网站建设
  • 外汇返佣网站建设有了域名之后怎么做自己的网站
  • 模板网站配置文件域名注册 网站建设 好做吗
  • 网站开发工具安卓版泉州网站建设方案服务
  • 长沙网站建设长沙网站制作淘宝优惠券网站建设总代
  • 河北云网站建设福建联泰建设集团网站
  • 基于单片机的蓝牙可调PWM波形发生器设计