c++深搜1-迷宫类问题

news2024/11/15 19:30:45

目录

1.问题引入

2.知识讲解

3.例题解析 

【例题1】迷宫的第一条出路。

题目描述

输入格式

输出格式

样例

输入数据#1

输出数据#1

输入数据#2

输出数据#2

【例题2】迷宫的所有路径。 

【例题3】走出迷宫的最少步数。 


1.问题引入

拓拓小时候玩迷宫游戏时,看到迷宫图都晕头转向,但是聪明的大佬却能每次飞快地找到一条迷宫路径,从而走出迷宫。

拓拓问大佬道:“大佬,你是怎么这么快找到迷宫的路径的?”

大佬道:“写个搜索代码,一下就找到了呀!”

拓拓立马急不可待,道:“大佬快教教我,我也要学!”

同学们,你能帮助拓拓找到右边迷宫的正确路径吗? 

2.知识讲解

下面总结一下基本的代码框架。全局状态变量

void dfs(当前状态) {
    if (当前状态是目标状态)          // 结束条件
        进行相应处理;
    for (所有可行的新状态) {        // 扩展新的状态
        if (新状态没有访问过 && 需要访问) {
            dfs(新状态);
        }
    }
}
int main() {
    ...
    dfs(初始状态);
    ...
}

 

3.例题解析 

【例题1】迷宫的第一条出路。

题目描述

已知一N×N的迷宫,允许往上、下、左、右四个方向行走,现请你按照左、上、右、下顺序进行搜索,也就是 (0,−1),(−1,0),(0,1),(1,0)(0,−1),(−1,0),(0,1),(1,0)。找出第一条从左上角到右下角的路径。

输入格式

输入数据有若干行,第一行有一个自然数N(N≤20),表示迷宫的大小,其后有N行数据,每行有N个0或1(数字之间没有空格,0表示可以通过,1表示不能通过),用以描述迷宫地图。入口在左上角(1,1)处,出口在右下角(N,N)处。所有迷宫保证存在从入口到出口的可行路径。

输出格式

输出数据仅一行,为按照要求的搜索顺序找到的从入口到出口的第一条路径(搜索顺序:左、上、右、下)。

样例

输入数据#1

4
0001
0100
0010
0110

Copy

输出数据#1

(1,1)->(1,2)->(1,3)->(2,3)->(2,4)->(3,4)->(4,4)

Copy

输入数据#2

4
0000
0111
0100
0000

Copy

输出数据#2

(1,1)->(2,1)->(3,1)->(4,1)->(4,2)->(4,3)->(3,3)->(3,4)->(4,4)

【问题分析】

首先需要一个g数组存放迷宫,还需要另一个rec数组存放走过的坐标(其他的数据存储方式也可以,只要能记录点的坐标即可)。那么我们每走到一个点,就可以记录到rec数组中去。因题目要求顺序是左、上、右、下,所以在(1,1)点只能优先向右,到达(1,2)点还是向右,到达(1,3)继续向右,到达(1,4)点,发现没法可走了,如下图所示。

当在(1,4)点发现无路可走时,自动返回到上一层的(1,3)点;同样地,在(1,3)点也无路可走,返回上一层的(1,2)点;在(1,2)点也无路可走,返回上一层的(1,1)点。注意,随着点的返回,也应当让rec数组返回到上一层,若有新的方向可走,就重新记录并覆盖掉之前的错误坐标。这样就可以保证rec数组中存放的是第一条可行的路径。

现在我们回到(1,1)点,还有往下走的方向可行,继续往下搜索。和上面一样,没走到一个新的点,就记录坐标到rec数组中,直到走到终点,就得到了第一条路径。

 

因为要求了行走顺序是左、上、右、下,所以走到(4,3)点时是向左,不能走(左边走过了),向上可以走;到达(3,3)点时,左和上都不能走,所以往右走;到达(3,4)点,左、上、右都不能走,只能往下走,最终到达了终点(4,4)。

总结一下思路:创建rec数组,存放路径。可以继续走的时候,存放下一个点放到rec数组中。当四个方向都不能走的时候,递归回到上一层,rec数组的记录的位置上移一层。这样,就可以覆盖掉错误的路径。当我们走到终点时,就结束。

我们可以用递归函数中的参数k记录到达rec的哪一层。dfs(x,y,k)表示向路径数组rec中第k行的位置,记录一个坐标(x,y)。在递归下一层时,和之前的深搜一样,要传参新的点和新的位置dfs(nx,ny,k+1),存放下一个点。 

参考代码如下:

#include <bits/stdc++.h>
using namespace std;
int n;
char a[25][25];  //存放迷宫
int rec[410][2];  //路径数组,存放迷宫的第一条正确路径
//方向数组:左、上、右、下
int dx[4] = {0, -1, 0, 1};
int dy[4] = {-1, 0, 1, 0};
bool flag = false;  //用于标记搜索结束
void display(int k) {  //自定义函数,用于输出第一条路径
    for (int i = 1; i <= k; i++) {
        cout << "(" << rec[i][0] << "," << rec[i][1] << ")";
        if (i != k) cout << "->";
    }
}
void dfs(int x, int y, int k) {
    rec[k][0] = x, rec[k][1] = y; //向rec的第k行记录当前点的坐标(x,y)
    a[x][y] = '1'; //修改当前点为'1',防止死循环和提高效率
    if (x == n && y == n) {
        display(k); //打印路径
        flag = true; //标记结束搜索
    }
    if(flag) return; //第一条路径找到了,就结束 if(flag) exit(0); 
    for (int i = 0; i < 4; i++) { //由点(x,y)扩展 新的点
        int nx = x + dx[i], ny = y + dy[i];
        if (a[nx][ny] == '0')  //新的点可以访问
            dfs(nx, ny, k + 1);
    }
}
int main() {
    cin >> n;//n行n列
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> a[i][j];
    dfs(1, 1, 1);//调用深搜函数
    return 0;
}

说明:

(1) rec数组至少要开辟400行,原因是因为最多会有20×20个坐标。

(2) 题目没有判断超过边界,是因为存放迷宫的数组是char类型的全局数组,全部初始化为'\0'(就是ASCII码值为0的字符)。同样地,第0行、第0列、第n+1行、第n+1列也会自动初始化为字符'\0',自然也就不等于字符'0'(ASCII码值为48)。

(3) 因为(2)的原因,建议不要使用0下标开始存放迷宫,而且数组要稍微开大一些。

【例题2】迷宫的所有路径。 

拓拓在森林里游玩,不小心走进了一个迷宫里面,这个迷宫是一个n行m列(n,m≤10)的矩阵,迷宫中有些格子是可以走的,有些格子是不能走的,能走的格子用“o”(小写字母o)表示,不能走的格子用“#”表示。拓拓选择走出迷宫的策略是:先向右,如果右边走不通则选择向下,如果下边走不通则选择向左,如果左边走不通则选择向上;如果四个方向都走不通,则后退选择其他能走的路径。拓拓从类似样例所示的迷宫的左上角(1,1)点进入迷宫(请注意:入口1,1和出口的n,m点都不是#),请问拓拓有哪些方法可以走出迷宫,走到(n,m)点;请编程求出所有可能的路径,输出这些路径,如果不存在任何的路径可以走出迷宫,请输出“no”。

【问题分析】

和上一题一样,我们创建a数组用来存放迷宫,创建rec数组存放路径。不同的是,这一题需要输出所有的路径,所以不能简单的把走过的路标记一下,还要考虑到后面新的路径。所以我们在调用dfs之前,把起点标记一下,然后在dfs的过程中,每次标记走过的点,当回到本层递归时,再取消标记。这样既能保证递归过程中不会往回走,还能保证所有的路径走完后,回到当前点还可以走新的路径。 

#include <bits/stdc++.h>
using namespace std;
char a[15][15];  //存图
int n, m, qx, qy, zx, zy;
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int rec[110][2];  //存放路径
int cnt;  //路径个数
void dfs(int x, int y, int k) {
    rec[k][0] = x, rec[k][1] = y;  //向rec数组中第k层存放点(x,y)
    if (x == n && y == m) {  //判断是否到达终点
        cout << ++cnt << ":";
        for (int i = 1; i < k; i++)
            cout << rec[i][0] << "," << rec[i][1] << "->";
        cout << rec[k][0] << "," << rec[k][1] << endl;
        return ;
    }
    for (int i = 0; i < 4; i++) {  //扩展新的点
        int nx = x + dx[i], ny = y + dy[i];
        if (a[nx][ny] == 'o') {  
            a[nx][ny] = '#';    //已经走过,标记为不可通行
            dfs(nx, ny, k + 1);
            a[nx][ny] = 'o';    //全部走完,取消标记
        }
    }
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];
    a[1][1] = '#';//注意:起始点需要在dfs之前标记为已走过=
    dfs(1, 1, 1);
    if (cnt == 0) cout << "no";
    return 0;
}

说明:(1) 题目中,可以不需要判断是否超过边界,原因和上一题的说明一样。

(2) main函数调用dfs函数之前,需要提前标记起点,否则在递归的过程中,可能再次回到起点,把起点存储两次。

(3) 因为是输出所有的路径,需要dfs函数把所有可能的情况都递归搜索一遍,所以不需要提前结束dfs函数。

【例题3】走出迷宫的最少步数。 

一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。空地格子用'.'表示,有障碍物的格子用'#'表示。计算步数要包括起点和终点。

输入数据:

4 
....
....
.###
....

输出数据:

17

【问题分析】

首先需要一个char数组存放迷宫,考虑到每次递归搜索都要记录起点到当前点的步数,我们可以再开辟一个step数组,记录从起点到任何一个点的最短距离,当所有的路径搜索完后,就可以得到起点到终点的最短距离了。 

如果走到某个点(x,y)需要的步数,比step数组中记录的最少步数还少,则按照新的方案走该点。注意到step数组的每个元素的初始值可以设置为INT_MAX。最终step数组记录了从起点到每个点至少需要多少步,step[n][m]就是最终结果。

#include<bits/stdc++.h>
using namespace std;
int r, c;
char a[50][50]; //地图
int step[50][50]; //存步数
int dx[] = {0, 0, 1, -1}; //没规定方向,保证是四个正确的方向即可
int dy[] = {1, -1, 0, 0};
void dfs(int x, int y, int k) {
    step[x][y] = k;  //记录到达(x,y)点的最短距离为k
    if (x == r && y == c) return ;  //搜到终点,提前结束本轮dfs
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i], ny = y + dy[i];
        // 确保新的路径走到点(nx,ny)的步数比之前step数组中存储的步数要小
        if (a[nx][ny] == '.' && k + 1 < step[nx][ny]) 
            dfs(nx, ny, k + 1);
    }
}
int main() {
    cin >> r >> c;
    for (int i = 1; i <= r; i++)
        for (int j = 1; j <= c; j++) {
            cin >> a[i][j];
            step[i][j] = INT_MAX;
        }
    dfs(1, 1, 1);
    cout << step[r][c];
    return 0;
}

说明:(1) int dx[],创建一维数组时不写大小,程序会自动分析赋值的内容开辟空间。

(2) k + 1 < step[nx][ny],此次的走法到达点(nx,ny)是k+1步,如果比之前某种最短的走法step[nx][ny]的步数更短,才选择这种走法。否则,不需要选择新的走法,选择之前最短的走法step[nx][ny]更好。

(3) 在调用搜索函数dfs之前,记得给step数组的所有元素赋值为INT_MAX。

(4) 调用dfs函数时,第三个参数初始应当给1,而不是0,是因为题目要求了起点也要计算步数。

(5) 最终step[r][c]存放的就是从左上角(1,1)到(r,c)的最短步数。实际上,到任何一点(如果能走到)的最短步数都算出来了。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/635163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小酷智慧地图3D导公众号小程序 v1.0.50+前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;阅读文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 小酷智慧地图3D导览系统适用于城市或乡镇全…

性能测试之全链路压测实战理论详解

目录 前言 01为什么需要全链路压测&#xff1f; 02全链路压测解决了哪些问题&#xff1f; 03哪些业务场景适合做 04基础技术组件 05小结 前言 要说当下研发领域最热门的几个词&#xff0c;全链路压测 肯定跑不了。最近的几次大会上&#xff0c;也有不少关于全链路的议题。…

Vue项目中使用MapboxGL开发3D地图教程(一)MapboxGL介绍以及前期vue项目的搭建

MapboxGL介绍以及前期vue项目的搭建 1、Mapbox-gl简介2、搭建vue项目2.1、创建vue项目2.2、注册mapbox官网2.3、mapbox-gl入门案例 3、Mapbox-gl地图主要配置参数说明 1、Mapbox-gl简介 Mapbox-gl是一个开源、基于webgl技术的前端地图类库。 地图数据渲染和可视化这块我们经常用…

第七章 详细设计

结构化程序设计的基本结构包括 顺序结构、选择结构、循环结构 三种。 详细设计层 详细设计概览 详细设计的任务&#xff1a;定义每一模块 三种控制结构&#xff1a;顺序、选择、循环 伪码 伪码是一种混合语言。外 部采用形式语言定义控制 结构和数据结构&#xff0c;内部使…

OpenGL光照之投光物

文章目录 平行光点光源衰减实现衰减 聚光手电筒平滑/软化边缘完整代码 我们目前使用的光照都来自于空间中的一个点。它能给我们不错的效果&#xff0c;但现实世界中&#xff0c;我们有很多种类的光照&#xff0c;每种的表现都不同。将光投射(Cast)到物体的光源叫做投光物(Light…

算法提高-图论- 最小生成树的扩展应用

最小生成树的扩展应用 最小生成树的扩展应用AcWing 1146. 新的开始AcWing 1145. 北极通讯网络AcWing 346. 走廊泼水节AcWing 1148. 秘密的牛奶运输 最小生成树的扩展应用 AcWing 1146. 新的开始 利用虚拟源点建图和原题的题意是等价的&#xff0c;因此我们使用虚拟源点建图套…

主从架构lua脚本-Redis(四)

上篇文章介绍了rdb、aof持久化。 持久化RDB/AOF-Redis&#xff08;三&#xff09;https://blog.csdn.net/ke1ying/article/details/131148269 redis数据备份策略 写job每小时copy一份到其他目录。目录里可以保留最近一个月数据。把目录日志保存到其他服务器&#xff0c;防止机…

服务器网站搭建(一)

文章目录 环境搭建域名购买和解析创建网站环境搭建 一台服务器,我买的腾讯云新加坡的。根据需求选择配置。 重置密码: 自己设置的密码要记住: 登录: 安装宝塔(centos系统),参考官网: https://www.bt

Python 接口测试框架

目录结构定义 首先来看一下项目整体的结构 框架结构 代码结构 Excel 文件结构 代码详细解析 1.工具包 tools 封装操作 excel 方法 excel_operation.py import xlrd from config.config import PROJECT_PATHclass OperationExcel:def __init__(self, file_nameNone, sheet_…

县村快递物流小程序 v1.2.63+前端(本站修复版)

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 核心功能&#xff1a; 1、县分拣中心入库出库 2…

什么是Vue的脚手架(Vue CLI)?

什么是Vue的脚手架&#xff08;Vue CLI&#xff09;&#xff1f; Vue.js 是一款流行的 JavaScript 框架&#xff0c;用来构建现代的单页面应用程序&#xff08;SPA&#xff09;。Vue.js 的核心库提供了丰富的功能和 API&#xff0c;但是创建一个完整的应用程序需要更多的工具和…

浏览器插件 | Font Picker - 网页字体识别工具

目录 软件简介 Font Picker插件背景 Font Picker插件离线安装教程 Font Picker 小结 软件简介 Font Picker 插件是一款用于 Chrome 浏览器的字体选择器&#xff0c;这种网页字体识别工具看起来非常的干净&#xff0c;使用起来也十分的简单。本文提供Font Picker网页字体…

d3dcompiler_43.dll文件丢失怎么修复,最新修复方法分享

本教程操作系统&#xff1a;Windows系统、 d3dcompiler_43.dll是电脑文件中的dll文件&#xff08;动态链接库文件&#xff09;。如果计算机中丢失了某个dll文件&#xff0c;可能会导致某些软件和游戏等程序无法正常启动运行&#xff0c;并且导致电脑系统弹窗报错。 在我们打开…

【LeetCode】110. 平衡二叉树

110. 平衡二叉树&#xff08;简单&#xff09; 思路 对二叉树做先序遍历&#xff0c;从底至顶返回子树最大高度&#xff0c;若判定某子树不是平衡树则“剪枝”直接向上返回。 递归返回值&#xff1a; 当节点 root 左、右子树的高度差 > 1&#xff1a;返回 -1&#xff0c;代…

吴恩达471机器学习入门课程1第1周

文章目录 1加载数据集2计算COST(均值平方差&#xff0c;1/2m(y_pre - y))3计算梯度4画出成本曲线5梯度下降 import math, copy import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyle) from lab_utils_uni import plt_house_x, plt_conto…

OpenMMLab-AI实战营第二期——4-2.MMDetection代码课

文章目录 1. MMDetection介绍&#xff08;vs MMSegmentation&#xff0c;mmdetection3d&#xff09;2. 代码2.1 使用pycocotools配合exif可视化图像2.2 mmdetection中config的继承2.3 mmdet的数据集可视化问题&#xff08;VISUALIZERS&#xff09;2.4 pin_memory参数设置2.5 ma…

康佳液晶电视(非智能)Mstar V56 芯片

&#xfffc;&#xfffc; &#xfffc; &#xfffc; &#xfffc; 2023/6/11 下午8:10:01 康佳用的地面波数字电视解码芯片是&#xff1f; &#xfffc; 2023/6/11 下午8:10:08 康佳电视使用的地面波数字电视解码芯片可能因不同的产品型号而有所不同。然而&#xff0c;根据…

【AI作画】使用DiffusionBee with stable-diffusion在mac M1平台玩AI作画

DiffusionBee是一个完全免费、离线的工具。它简洁易用&#xff0c;你只需输入一些标签或文本描述&#xff0c;它就能生成艺术图像。 DiffusionBee下载地址 运行DiffusionBee的硬性要求&#xff1a;MacOS系统版本必须在12.3及以上 DBe安装完成后&#xff0c;去C站挑选自己喜欢…

ur机器人在moveit中运行环境搭建(保姆级)

ur机器人在moveit中运行环境搭建 我的系统是ubuntu20.04 rosb版本: noetic 1.安装运动学插件 sudo apt-get install ros-noetic-trac-ik-kinematics-plugin 2.安装 eigenpy 需要单独编译,EigenPy是一个用于在Python中使用Eigen库的绑定库 git clone https://github.com/…

[MAUI]写一个跨平台富文本编辑器

文章目录 原理创建编辑器定义实现复合样式选择范围字号字体颜色与背景色字体下划线字体加粗与斜体 序列化和反序列化跨平台实现集成至编辑器 创建控件使用控件最终效果已知问题项目地址 富文本编辑器是一种所见即所得(what you see is what you get 简称 WYSIWYG)文本编辑器&am…