预告:我用两年写的新书《算法竞赛》,已于2022年2月交给清华大学出版社,预计于2022年7月出版。
《算法竞赛》是一本“大全”,内容覆盖“基础-中级-高级”,篇幅700页左右。部分知识点的草稿已经在本博客发表。
本篇博客节选自新书《算法竞赛》的“3.4 BFS与最短路”。

文章目录

  • 1、BFS求最短路
  • 2、路径打印的简单方法
  • 3、路径打印的标准方法

1、BFS求最短路

   最短路问题是最有名的图论问题,有很多不同的场景和算法。
   在一种特殊的场景中,BFS也是极为优秀的最短路算法,这种场景就是:所有的相邻两个点的距离相等,一般把这个距离看成1。此时BFS是最优的最短路径算法,查找一次从起点到终点的最短距离的计算复杂度是O(m) ,m是图上边的数量,因为需要对每条边做一次检查。
   如果两点之间距离不相等,就不能用BFS了,需要用Dijkstra等通用算法。
   BFS的特点是逐层扩散,也就是按最短路扩散出去。往BFS的队列中加入邻居结点时,是按距离起点远近的顺序加入的:先加入距离起点为1的邻居结点,加完之后,再加入距离为2的邻居结点,等等。搜完一层,才会继续搜下一层。一条路径是从起点开始,沿着每一层逐步往外走,每多一层,路径长度就增加1。那么,所有长度相同的最短路径都是从相同的层次扩散出去的。
   求最短路径时,常见的问题有两个:
   (1)最短路径有多长?答案显然是唯一的;
   (2)最短路径经过了哪些点?由于最短路径可能不只一条,所以题目往往不要求输出,如果要求输出,一般是要求输出字典序最小的那条路径。
   下面用一道例题介绍最短路径的计算和最短路径的打印。


2019年省赛真题 迷宫 https://www.lanqiao.cn/problems/602/learning/
题目描述:下图给出了一个迷宫的平面图,其中标记为1 的为障碍,标记为0 的为可以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这个它的上、下、左、右四个方向之一。对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,一共10 步。其中D、U、L、R 分别表示向下、向上、向左、向右走。对于下面这个更复杂的迷宫(30 行50 列),请找出一种通过迷宫的方式,其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中D<L<R<U。


  本题是基本的BFS搜索最短路。BFS是最优的算法,每个点只需搜一次,即进队列和出队列各一次。
  题目要求返回字典序最小的最短路径,那么只要在每次扩散下一层(往BFS的队列中加入下一层的结点)时,都按字典序“D<L<R<U”的顺序来加下一层的结点,那么第一个搜到的最短路径就是字典序最小的。
  本题的关键是路径打印,下面给出两种打印方法。

2、路径打印的简单方法

  简单方法,适合小图。
  每扩展到一个点v,都在v上存储从起点s到v的完整路径path。到达终点t时,就得到了从起点s到t的完整路径。
  在下面的代码中,在每个结点上记录从起点到这个点的路径。到达终点后,用cout<<now.path<<endl;就打印出了完整路径。
  这样做的缺点是会占用大量空间,因为每个点上都存储了完整的路径。

#include<bits/stdc++.h>
using namespace std;
struct node{int x;int y;
//(1)简单方法:string path;    //path,记录从起点(0,0)到这个点(x,y)的完整路径
};
char mp[31][51];     //用矩阵存地图
char k[4]={'D','L','R','U'};  //字典序
int dir[4][2]={{1,0},{0,-1},{0,1},{-1,0}};   //4个方向
int vis[30][50];        //标记。vis=1: 已经搜过,不用再搜void bfs(){node start; start.x=0;  start.y=0;
//(1)简单方法:start.path="";vis[0][0]=1;               //标记起点被搜过queue<node>q;q.push(start);             //把第一个点放进队列,开始BFSwhile(!q.empty()){node now = q.front();  //取出队首q.pop();if(now.x==29 && now.y==49){ //第一次达到终点,这就是字典序最小的最短路径
//(1)简单方法:打印完整路径cout << now.path << endl;return;}for(int i=0;i<4;i++){  //BFS:扩散邻居结点node next;next.x = now.x + dir[i][0];  next.y = now.y + dir[i][1];if(next.x<0||next.x>=30||next.y<0||next.y>=50)  //越界了continue;if(vis[next.x][next.y]==1 || mp[next.x][next.y]=='1')continue;           //vis=1:已经搜过;  mp=1:是障碍vis[next.x][next.y]=1;  //标记被搜过
//(1)简单方法:记录完整路径:复制上一个点的路径,加上这一步next.path = now.path + k[i];q.push(next);}}
}
int main(){for(int i=0;i<30;i++)  cin >> mp[i];  //读题目给的地图数据bfs();
}

3、路径打印的标准方法

  标准方法,适合大图。
  其实不用在每个结点上存储完整路径,而是在每个点上记录它的前驱结点就够了,这样从终点能一步步回溯到起点,得到一条完整路径。称这种路径记录方法为“标准方法”。
  注意看代码中的print_path(),它是递归函数,先递归再打印。从终点开始,回溯到起点后,再按从起点到终点的顺序,正序打印出完整路径。

#include<bits/stdc++.h>
using namespace std;
struct node{int x;int y;
};
char mp[31][51];  //存地图
char k[4]={'D','L','R','U'};  //字典序
int dir[4][2]={{1,0},{0,-1},{0,1},{-1,0}};
int vis[30][50];  //标记。vis=1: 已经搜过,不用再搜//(2)标准方法:
char pre[31][51];        //   用于查找前驱点。例如pre[x][y] = ‘D’,表示上一个点//往下走一步到了(x,y),那么上一个点是(x-1,y)
void print_path(int x,int y){      //打印路径:从(0,0)到(29,49)if(x==0 && y==0)    return;      //回溯到了起点,递归结束,返回if(pre[x][y]=='D')  print_path(x-1,y);   //回溯,往上 Uif(pre[x][y]=='L')  print_path(x,  y+1); //回溯,往右 Rif(pre[x][y]=='R')  print_path(x,  y-1);if(pre[x][y]=='U')  print_path(x+1,y);printf("%c",pre[x][y]);                  //最后打印的是终点
}
void bfs(){node start; start.x=0;  start.y=0;vis[0][0]=1;               //标记起点被搜过queue<node>q;q.push(start);             //把第一个点放进队列,开始BFSwhile(!q.empty()){node now = q.front();  //取出队首q.pop();if(now.x==29 && now.y==49){ //第一次达到终点,这就是字典序最小的最短路径
//(2)标准方法:打印完整路径,从终点回溯到起点,打印出来是从起点到终点的正序print_path(29,49);return;}for(int i=0;i<4;i++){  //扩散邻居结点node next;next.x = now.x + dir[i][0];  next.y = now.y + dir[i][1];if(next.x<0||next.x>=30||next.y<0||next.y>=50)  //越界了continue;if(vis[next.x][next.y]==1 || mp[next.x][next.y]=='1')continue;           //vis=1:已经搜过;  mp=1:是障碍vis[next.x][next.y]=1;  //标记被搜过
//(2)标准方法:记录点(x,y)的前驱pre[next.x][next.y] = k[i];q.push(next);}}
}
int main(){for(int i=0;i<30;i++)  cin >> mp[i];  //读题目给的地图数据bfs();
}

BFS最短路径的两种打印方法相关推荐

  1. HTML局部打印,区域打印的两种实现方法总结

    在开发中,有时需要实现页面的局部打印功能,不打印页面上不需要的区域, 例如页面: <div>aaaaaaaaaaaaaaaaaaa</div><div>bbbbbb ...

  2. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  3. php 有几种打印方法,php 5种打印方式及变量类型,

    php 5种打印方式及变量类型五种打印方法和可变类型的PHP, PHP 打印的5种类型 echo 直接打印,无返回值.Echo一次可以打印多个字符串,print只能打印一个字符串.回声打印数据比打印速 ...

  4. day030进程的两种创建方法,验证进程的空间隔离,join等待子进程

    本节内容: 1.操作系统的简单介绍 2.进程的两种创建方法 3.进程之间是空间隔离的, 参考文章: 一.操作系统的简单介绍 1.操作系统简单介绍 操作系统就是一个协调.管理和控制计算机硬件资源和软件资 ...

  5. OCR图片转文字两种python方法实现

    图片转文字的两种处理方法: 一种是文字识别工作都需要在网络侧完成的方式,我们称为在线识别: 另一种是不需要互联网功能的,我们称作离线识别. 在线识别方式 先看第一种,在线识别的方式.在线识别方式最大的 ...

  6. 图——最短路径的两种算法

    最短路径 在生活中,例如,当我们坐公交或轻轨时,都会看一下交通图,找到在哪个站下是最快能达到目的地的,也就是路径最小.考虑到交通图的有向性,例如汽车的上山下山.轮船的顺水逆水,所花费的时间或代价就不相 ...

  7. linux shell语法检查或者查看shell脚本执行过程的参数介绍及两种使用方法

    shell语法检查或者查看shell脚本执行过程的参数介绍及两种使用方法 一.常用参数概述: set -x 与 set +x 在liunx脚本中可用set -x就可有详细的日志输出,省的老是要echo ...

  8. 用两种遍历方法判断图中两点是否有路径

    用两种遍历方法判断图中两点是否有路径(可直接测试) 邻接表.图.图的两种遍历以及图中路径的基本概念,可以去自行了解和学习(下面是代码实践)可直接在自己主机测试 #include <iostrea ...

  9. java 贪心算法思路,贪心算法之——黑白点的匹配(两种实现方法),贪心算法...

    贪心算法之--黑白点的匹配(两种实现方法),贪心算法 一.题目 设平面上分布着n个白点和n个黑点,每个点用一对坐标(x, y)表示.一个黑点b=(xb,yb)支配一个白点w=(xw, yw)当且仅当x ...

  10. 贪心算法黑白点匹配C语言,贪心算法之——黑白点的匹配(两种实现方法)

    一.题目 设平面上分布着n个白点和n个黑点,每个点用一对坐标(x, y)表示.一个黑点b=(xb,yb)支配一个白点w=(xw, yw)当且仅当xb>=xw和yb>=yw. 若黑点b支配白 ...

最新文章

  1. 丙类放大电路实验报告_电子工程师入门基础:那些关于电子电路设计的基础知识...
  2. windows boot files
  3. android两张图片切换,android recyclerview 切换列表视图
  4. 柯南君:看大数据时代下的IT架构(4)消息队列之RabbitMQ--案例(Helloword起航)...
  5. Comet OJ(Contest #8)-C符文能量【dp】
  6. nginx源码学习资源
  7. netty大白话--字符串的收发(三)
  8. 离开北上广的互联网工程师最终都去了哪里?
  9. 获取Android 光感Sensor的值
  10. abaqus python 读取文件_通过Python脚本从Abaqus中的excel文件导入幅度数据
  11. Android编译判定BoardConfig.mk的宏控是否打开或者有效的验证方法
  12. 青岛市新添智能服务平台 借力大数据智慧城市再扩容
  13. 图片批量加边框,详细操作步骤
  14. 初始化oracle环境失败,Oracle登录显示无法初始化
  15. html5 logo背景透明度,使用纯洁的CSS实现HTML5的新logo
  16. Redis的ZSET的实现及结合源码的跳跃表结构分析
  17. 清华姚班陈丹琦获斯隆奖!博士毕业论文是近十年最热之一
  18. Linux的ssh客户端
  19. JavaSE学习笔记(一)基础知识
  20. Makefile详解——从入门到精通

热门文章

  1. server服务器系统2019安装,windowsserver 2019系统安装教程图文详解
  2. 武汉大学java 期末考试试题_武汉大学java期末考试试题和答案
  3. 每个开发人员都应该学习的5种编程语言(上)
  4. 展讯通信:文章紫光收购后展讯困难重重”失实
  5. Elastic Stack 开源的大数据解决方案
  6. CS:GO开服架设服务器搭建游戏配置方法教程教学插件配置下载资源配置
  7. IIS7.0 CSS、JS、图片报500错误
  8. GitHub / 码云 Pages 打造个人在线简历
  9. PHPUnit简介及使用
  10. r语言是高级编程语言_R编程语言介绍