DFS搜索和输出所有路径

news2025/1/10 20:44:25

文章目录

  • 1、DFS搜所有路径
  • 2、用栈记录和输出路径
  • 3、例题
    • 3.1 C++代码
    • 3.2 Python代码
  • 4、真题
    • 4.1 C++代码
    • 4.2 Python代码

  2022.12将出版蓝桥杯大赛用书《蓝桥杯大赛-程序设计竞赛专题挑战教程》,作者:蓝桥杯组委会、罗勇军。
  这本书解析了蓝桥杯大赛的常见考点,所有例题用C/C++、Python两种语言给出代码,适合C/C++组和Python组入门。
  本文的内容摘抄自这本书。

1、DFS搜所有路径

  DFS是一种暴力搜索,它能搜所有可能的情况。
  蓝桥杯大赛常常涉及到路径问题,需要用DFS寻找路径和输出路径,一般是输出一条符合要求的路径。
  DFS的特点是“一路到底”,遇到终点时,就得到了从起点到终点的一条路径。所以搜所有路径用DFS写代码最方便。下面看DFS如何找到所有的路径。
  图中,'@‘表示起点,’*‘表示终点,’.‘是可以走的点,’#'是墙壁不能走。
在这里插入图片描述

  图(1)是搜到的第一条路径。从起点(1, 1)出发,查询它的“左-上-右-下”哪个方向能走,发现“左-上”不能走,可以走“右”。第一步走到右边的(2,1)。然后从(2, 1)继续走,它可以向上走到(2, 0),但是后面就走不通了,退回来改走下面的(2, 2)。按这个方法,逐步深入走到终点,最后得到一条从起点(1, 1)到终点(0, 2)的路径“(1,1)->(2,1)->(2,2)->(1,2)->(0,2)”。后面C++代码中的a[nx][ny] = '#'的作用是“保存现场”。在这次DFS深入过程中,这条路径上曾经路过的点被“保存现场”,不允许再次经过。到达终点后,从终点退回,回溯寻找下一个路径。代码中的a[nx][ny] = '.'的作用是退回后“恢复现场”。
  图(2)是搜到的第二条路径。在图(1)搜到一条路径后,从终点(0, 2)退回到(1, 2),继续向下走到(1, 3)、(0, 3)、(0, 2)。
  图(3)是搜到的第三条路径。从终点(0, 2)一路退回到(2, 2)后,才找到新路径。在退回的过程中,原来被“保存现场的”(0, 3)、(1, 3)、(0, 2),重新被“恢复现场”,允许被经过。例如(1, 3),在第二条路径中曾用过,这次再搜新路径时,在第三条路径中重新经过了它。如果不“恢复现场”,这个点就不能在新路径中重新用了。
  总结“保存现场”和“恢复现场”的作用:
  (1)“保存现场”的作用,是禁止重复使用。当搜索一条从起点到终点的路径时,这条路径上经过的点,不能重复经过,否则就兜圈子了,所以路径上的点需要“保存现场”,禁止再经过它。没有经过的点,或者碰壁后退回的点,都不能“保存现场”,这些点可能后面会进入这条路径。
  (2)“恢复现场”的作用。当重新搜新的路径时,方法是从终点(或碰壁的点)沿着旧路径逐步退回,每退回一个点,就对这个点“恢复现场”,允许新路径重新经过这个点。例如图(2)和图(3)的点(1, 3)。

2、用栈记录和输出路径

  在DFS中,用栈来跟踪DFS的过程,记录走过的路径,是最方便的。DFS到某个格子时,把这个格子放到栈里,表示路径增加了这个格子。DFS回溯的时候,退出了这个格子,表示路径上不再包括这个格子,需要从栈中弹走这个格子。

3、例题

  下面是本文自创的例题,演示上述的两个功能:
  (1)用DFS搜索从起点到终点的所有路径;
  (2)用栈记录路径。


例题:输出所有路径
题目描述】给出一张格子图,输出从起点到终点的所有路径。
输入描述】第一行是整数n,m,分别是行数和列数。后面n行,每行m个符号。只有两种符号:’•’能走,’#’是墙壁不能走。在每一步,都按左-上-右-下的顺序搜索。左上角是起点,坐标(0, 0),右下角是终点,坐标(n-1, m-1)。
输出描述】输出所有的路径。为了方便表示,约定每个小格子用一个数字代表,从西北角(左上角)开始编号: 0, 1, 2, 3…
样例输入1

3 2
..
..
..

样例输出1

0 2 4 5 
0 2 3 5 
0 1 3 2 4 5 
0 1 3 5

样例输入2

3 4
....
..#.
....

样例输出2

0 4 8 9 5 1 2 3 7 11 
0 4 8 9 10 11 
0 4 5 1 2 3 7 11 
0 4 5 9 10 11 
0 1 5 4 8 9 10 11 
0 1 5 9 10 11 
0 1 2 3 7 11 

3.1 C++代码

  下面代码中的函数dfs()搜索出所有代码,并打印出来。
  vector<int>path是一个栈,用于记录路径。在dfs()函数的开始,到达出口时,就根据path打印出路径。

#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[25][25];   //存储格子图
vector<int> path;                    //path是一个栈,记录路径
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};   //4个方向:左、上、右、下

void dfs(int x,int y){
    if(x==n-1 && y==m-1){          //终止条件:到达出口
         for(int i = 0;i < path.size();i++)
            cout << path[i] <<" ";
         cout<<endl;
         return;
    }
    for(int i = 0;i < 4;i++){
        int nx = x + dir[i][0],ny = y + dir[i][1];
        if(nx>=0 && nx<n && ny>=0 && ny<m && a[nx][ny]=='.'){
            a[nx][ny]='#';
            path.push_back(nx*m + ny); //进栈,记录路径
            dfs(nx,ny);
            path.pop_back();           //出栈,DFS回溯
            a[nx][ny]='.';
        }
    }
}
int main(){
    cin >> n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            cin>>a[i][j];
    path.push_back(0);
    a[0][0]='#';
    dfs(0,0);
    return 0;
}

3.2 Python代码

  下面的Python代码和上面的C++代码逻辑一样。注意它使用栈的方法,用一个list数组 path=[]模拟栈。

def dfs(x,y):    
    if x==n-1 and y==m-1:    #到达终点
        for i in range(len(path)): print(path[i],end=' ')
        print()  #换行
        return
    dir = ((-1, 0), (0, -1),(1, 0),  (0, 1)) #4个方向:左、上、右、下
    for u,v in dir:  #遍历4个方向的邻居
        nx = x+u; ny = y+v    #邻居的坐标
        if 0<=nx<n and 0<=ny<m and a[nx][ny]=='.':    
            a[nx][ny]='#'          #保存现场:这个点在这次更深的dfs中不能再用
            path.append(nx*m + ny) #进栈,把这个点记录到路径里
            dfs(nx, ny)
            path.pop();            #出栈,释放这个点
            a[nx][ny]='.'          #恢复现场:回溯后这个点可以再次用

n,m = map(int, input().split())    #n行、m列
a=[]
for i in range(n):                 #读图,存到二维矩阵a[n][m],n行m列
    a.append(list(input()))
path = []                          #用栈记录路径
path.append(0)
a[0][0]='#'
dfs(0,0)

4、真题

  下面的真题,是上面讲解的知识点的直接应用。


路径之谜 2016年第七届决赛,lanqiaoOJ第89题 https://www.lanqiao.cn/problems/89/learning/
题目描述】小明冒充X星球的骑士,进入了一个奇怪的城堡。城堡里边什么都没有,只有方形石头铺成的地面。假设城堡地面是n×n个方格。按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有n个靶子)同一个方格只允许经过一次。但不必走完所有的方格。如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)
输入格式】第一行一个整数N(0<N<20),表示地面有N×N个方格;第二行N个整数,空格分开,表示北边的箭靶上的数字(自西向东);第三行N个整数,空格分开,表示西边的箭靶上的数字(自北向南)。
输出格式】一行若干个整数,表示骑士路径。为了方便表示,我们约定每个小格子用一个数字代表,从西北角(左上角)开始编号: 0, 1, 2, 3…


  又是一道格子搜索题,这种题在蓝桥杯大赛中很常见。
  题目要求输出一条路径,用DFS是很合适的,DFS搜索过程中,自然生成一条路径。
  每走到一个格子,对应的靶子上箭多一支,靶子上的箭等于给定的数字后,就不用再DFS下去了。这是一个简单的剪枝。
  注意DFS时记录路径的技巧。根据题目的要求,用栈来跟踪DFS的过程,记录DFS走过的路径,是最方便的。DFS到某个格子时,把这个格子放到栈里,表示路径增加了这个格子。DFS回溯的时候,退出了这个格子,表示路径上不再包括这个格子,需要从栈中弹走这个格子。

4.1 C++代码

#include<bits/stdc++.h>
using namespace std;
int n;
int a[25],b[25],vis[25][25];
vector<int> path;                  //记录路径
int d[4][2]={1,0,-1,0,0,1,0,-1};   //上下左右
void dfs(int x,int y){
    if(a[x]<0 || b[y]<0) return;   //剪枝
    if(x==n-1 && y==n-1){          //走到出口
        int ok=1;
        for(int i = 0;i < n;i++)
            if(a[i]!=0 || b[i]!=0){ ok=0; break;}
        if(ok)
            for(int i = 0;i < path.size();i++)   cout << path[i] <<" ";
    }
    for(int i = 0;i < 4;i++){
        int tx = x + d[i][0],ty = y + d[i][1];
        if(vis[tx][ty]==0 && tx>=0 && tx<n && ty>=0 && ty<n){
            vis[tx][ty]=1;
            path.push_back(tx*n + ty); //进栈,记录路径
            a[tx]--;    b[ty]--;
            dfs(tx,ty);
            path.pop_back();           //出栈,DFS回溯
            a[tx]++;    b[ty]++;
            vis[tx][ty]=0;
        }
    }
}
int main(){
    cin >> n;
    for(int i=0;i<n;i++) cin >> b[i];  //北面靶子
    for(int i=0;i<n;i++) cin >> a[i];  //西面靶子
    path.push_back(0);
    vis[0][0]=1;
    a[0]--;  b[0]--;
    dfs(0,0);
    return 0;
}

4.2 Python代码

def dfs(x,y):
    if a[x]<0 or b[y]<0: return
    if x==n-1 and y==n-1:
        ok=1
        for i in range(n):
            if a[i]!=0 or b[i]!=0: ok=0; break
        if ok==1:
            for i in range(len(path)): print(path[i],end=' ')
    for d in [(1,0),(-1,0),(0,1),(0,-1)]:
        tx = x+d[0]; ty = y+d[1]
        if 0 <= tx < n and 0 <= ty < n and vis[tx][ty] == 0:
                vis[tx][ty] = 1
                path.append(tx*n + ty)      #进栈,记录路径
                a[tx] -= 1; b[ty] -= 1
                dfs(tx, ty)
                path.pop();                 #出栈,DFS回溯
                a[tx] += 1;  b[ty] += 1
                vis[tx][ty] = 0

n = int(input())
vis = [[0]*n for i in range(n)]
path = []               #用栈记录路径
path.append(0)
b = list(map(int,input().split()))
a = list(map(int,input().split()))
vis[0][0]=1
a[0] -= 1;  b[0] -= 1
dfs(0,0)

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

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

相关文章

PLC学习笔记(二):PLC结构(1)

目录&#xff1a; PLC学习笔记&#xff08;一&#xff09;&#xff1a;概述 PLC学习笔记&#xff08;二&#xff09;&#xff1a;PLC结构&#xff08;1&#xff09; PLC学习笔记&#xff08;三&#xff09;&#xff1a;PLC结构&#xff08;2&#xff09; &#x1f981;&…

嵌入式FreeRTOS学习十一,深入理解任务调度机制

一.任务调度机制 可抢占&#xff1a;高优先级的任务先运行时间片轮转&#xff1a;同优先级的任务轮流执行空闲任务礼让&#xff1a;如果有同是优先级为0的其他就绪任务&#xff0c;空闲任务主动放弃一次运行机会函数调用vTaskDelay( xDelay5ms )可以主动放弃任务执行&#xff…

大学物理·第8章【电磁感应、电磁场】

from now on, 以后的篇幅我都不会写太多了。因为效果也不一定有&#xff0c;还很浪费时间。 感应电动势 那个奥特曼变身器的符号是 磁链 看看划线公式即可 动生电动势&#xff1a;dξ vB dl 例 如果&#xff0c;到时候复习的时候觉得简单的话&#xff0c;就删掉好了 自感、互…

【HTML5】登录页面制作简易版

刚开始学习Java。文件的命名&#xff0c;讲道理应该以英文为主&#xff0c;但是英语又不好&#xff0c;所以只好用拼音&#xff0c;最痛苦的应该算是那些英语又不好&#xff0c;又想秀一下的程序员&#xff0c;一半英语一半拼音&#xff0c;如mainFangFa~~~你说看了糟心不糟心。…

山东大学线性代数-1-矩阵-1

目录 1.2 几种特殊的矩阵 1.2.1 方阵 1.2.2 零矩阵 1.2.3 对角矩阵 1.2.4 单位矩阵 1.2.5 数量矩阵 1.2.6 三角阵 1.2.7 梯形阵 1.3 矩阵的运算&#xff08;一&#xff09; 1.3.1 相等 1.3.2 加、减法 1.3.3 数乘 1.3.4 矩阵的乘法 1.4 矩阵的运算&#xff08;二&#x…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.18 查询文档

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.18 查询文档4.18.1 查询文档4.18.2 小结4.18.3 总…

山东大学线性代数-3-n维向量

目录 3.1 n维向量及其线性运算 3.1.1 n维向量的概念 3.1.2 向量的相等 3.1.3 n维向量的线性运算 3.1.4 线性组合 3.1.5 向量组的等价 3.2 向量组的线性相关性 3.2.1 线性相关性的定义 3.2.2 特殊情形的线性相关性 3.3 相关性的判定及有关重要结论 3.3.1 线性相关与…

[MQ] SpringBoot使用直连交换机Direct Exchange

✨✨个人主页:沫洺的主页 &#x1f4da;&#x1f4da;系列专栏: &#x1f4d6; JavaWeb专栏&#x1f4d6; JavaSE专栏 &#x1f4d6; Java基础专栏&#x1f4d6;vue3专栏 &#x1f4d6;MyBatis专栏&#x1f4d6;Spring专栏&#x1f4d6;SpringMVC专栏&#x1f4d6;SpringBoot专…

Bootstrap-栅格实例(二)

栅格实例 进入官网 选择3.0的中文文档&#xff1a; 选择组件&#xff1a; 选择缩放图&#xff1a; 选择这个&#xff0c;复制代码&#xff1a; 把代码拷贝到&#xff0c;新创建的模板固定container里面&#xff1a; 删除靠过来的多余的row&#xff1a; 修改div的calss&…

【信息奥赛实训】Week1——STL 与基础数据结构专题训练

【信息奥赛实训】Week1-Lab-STL 作者&#xff5c;Rickyの水果摊 时间&#xff5c;2022年11月20日 实训概要 实训专题 STL 与基础数据结构专题训练 实训目的 掌握STL常用的算法、容器、容器适配器的使用方法。能够利用STL的算法、容器、容器适配器求解问题。 题目列表 A&…

HTML小游戏9 —— 潜行游戏《侠盗罗宾汉》(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 风趣幽默的前端学习课程&#xff1a;&#x1f449;28个案例趣学前端&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计算机相关知…

紫外线杀菌器

一、概述 紫外线杀菌器是利用波长为225-275nm的紫外线对微生物的杀灭而使水中菌类得以净化。当水流经消毒仪时&#xff0c;高强度杀菌作用的紫外线即将水中细菌杀灭。本产品彻底解决了饮用水二次污染细菌超标问题&#xff0c;同时也适用于饮料、食品、游泳池等用水的消毒处理。…

Linux基本指令——综合操作

bc指令bc指令其实就是Linux下的计算器Ctrl c退出也可以通过管道进行输出。bc命令并不常用&#xff0c;主要是见见。uname -r指令语法&#xff1a;uname [选项]作用&#xff1a;uname可以用来获取电脑或者操作系统的相关信息。选项&#xff1a;-a或–all 详细输出所有信息&…

ConsulManager安装

地址 地址&#xff1a; https://github.com/starsliao/ConsulManager使用yum部署consul 在这里可以直接使用yum安装部署consul这个组件 # 使用yum部署consul yum install -y yum-utils yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.re…

(续)SSM整合之springmvc笔记(RESTful之RESTful案例)(P148-153)

目录 RESTful案例 一 . 准备工作 1 . 准备实体类 2 .准备dao模拟数据 3. 创建控制层EmployeeController 二 功能清单 三 . 具体功能&#xff1a;查询所有员工数据 ①控制器方法 ②创建employee_list.html RESTful案例 一 . 准备工作 和传统CRUD 一样&#xff0c;…

【前沿技术RPA】 一文了解UiPath 通过Invoke Method 和 Invoke Code增强自动化功能

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…

[附源码]Python计算机毕业设计_旅游系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

多表操作(外键)

多表操作 外键 外键&#xff08;foreign key&#xff09;&#xff1a; 外键为某个表中的一列&#xff0c;它包含另一个表的主键值&#xff0c;定义了两个表之间的关系。 主表&#xff08;父表&#xff09;&#xff1a;对于两个具有关联关系的表而言&#xff0c;相关联字段中…

HTML+CSS+JS网页设计期末课程大作业____(航天月球响应式 3页)

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 公司官网网站 | 企业官网 | 酒店官网 | 等网站的设计与制 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&#…

【Java八股文总结】之Spring

文章目录Spring一、Spring介绍1、Spring是什么&#xff1f;特性&#xff1f;2、Spring有哪些模块&#xff1f;3、Spring中的设计模式&#xff1f;二、IOC1、基本概念2、什么是IOC&#xff1f;什么是DI&#xff1f;Q&#xff1a;为什么要使用 IOC 呢&#xff1f;3、Spring IOC的…