深度优先搜索 - 最短路径

迷宫由 nm 列的单元格组成 (nm 都小于等于 50),每个单元格要么是空地,要么是障碍物。障碍物是不能通行的,要求从迷宫起点开始找到一条通往迷宫中目标位置的最短路径。

使用一个二维数组来存储迷宫,刚开始假设从迷宫入口处 (1, 1) 开始,目标位置在 (ph, qw),找到从 (1, 1)(ph, qw) 的最短路径。如果刚开始在入口的点 (1, 1),那么下一步只能是往右走或者往下走。如果是到了迷宫中间的某个点,那么可能是往上、下、左和右四个方向走,只能一个个去尝试。这里定义一个顺序,按照顺时针的方向逐个尝试 (即按照右、下、左、上的顺序)。

从迷宫入口处 (1, 1) 开始,一步之内可以达到的点只有 (1, 2)(2, 1)。根据刚才的策略,我们先往右边走,来到 (1, 2) 这个点。来到 (1, 2) 之后只能到达 (2, 2) 这个点。因为 (1, 3) 是障碍物无法到达,(1, 1) 是刚才来的路径中已经走过的点,也不能走,所以只能到 (2, 2) 这个点。但是目标位置并不在 (2, 2) 这个点上,所以还得继续往下走,直至无路可走或者找到目标位置为止。请注意,并不是找到目标位置就结束了。刚才仅仅尝试了一条路,而这条路并不一定是最短的。刚才很多地方在选择方向的时候都有多种选择,因此我们需要返回到这些地方继续尝试往别的方向走,直到把所有可能都尝试一遍,最后输出最短的一条路径。下图就是一种可行的搜索路径。

现在尝试用深度优先搜索来解决这个问题,dfs() 函数的功能是解决当前应该怎么办。到达某个点的时候需要处理的是:先检查是否已经到达目标位置,如果没有到达则找出下一步可以走的地方。为了解决这个问题,此处 dfs() 函数需要维护 4 个参数,分别是地图,当前这个点的 yh 坐标、xw 坐标以及当前已经走过的步数 step

void dfs(int map_data[50][50], int yh, int xw, int step)
{return;
}

判断是否已经到达目标位置很好实现,只需要判断当前的坐标和目标位置的坐标是否相等就可以了,如果相等则表明已经到达目标位置。

void dfs(int map_data[50][50], int yh, int xw, int step)
{// 判断是否到达目标位置if ((yh == end_yh) && (xw == end_xw)){if (step < min_step) {min_step = step; // 更新最少步骤}return; // 返回}return;
}

如果没有到达目标位置,则找出下一步可以走的地方。因为有四个方向可以走,根据我们之前的约定,按照顺时针的方向来尝试 (即按照右、下、左、上的顺序)。为了编程方便,定义了一个方向数组 next

 // right, down, left, up - (height, width)int next[4][2] = { { 0, 1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };

通过这个方向数组,使用循环就很容易获得下一步的坐标。这里将下一步的横坐标用 txw 存储,纵坐标用 tyh 存储。

// 枚举 4 种走法
for (int k = 0; k < 4; k++)
{// 计算下一个点的坐标tyh = yh + next[k][0];txw = xw + next[k][1];
}

接下来我们就要对下一个点 (tyh, txw) 进行判断,包括是否越界,是否为障碍物,以及这个点是否已经在路径中 (即避免重复访问一个点)。需要用 book[tyh][txw] 来记录格子 (tyh, txw) 是否已经在路径中。

如果这个点符合所有的要求,就对这个点进行下一步的扩展,即 dfs(map_data, tyh, txw, step + 1);,注意这里是 step + 1,因为一旦你从这个点开始继续往下尝试,就意味着你的步数已经增加了 1。

 // 枚举 4 种走法for (int k = 0; k < 4; k++){// 计算下一个点的坐标tyh = yh + next[k][0];txw = xw + next[k][1];// 判断是否越界if ((tyh < 0) || (tyh >= YH) || (txw < 0) || (txw >= XW)) {continue;}// 0 表示空地,1 表示障碍物if ((0 == map_data[tyh][txw]) && (0 == book[tyh][txw])){book[tyh][txw] = 1; // 标记这个点已经走过dfs(map_data, tyh, txw, step + 1); // 开始尝试下一个点book[tyh][txw] = 0; // 尝试结束,取消这个点的标记}}

1. D:\visual_studio_workspace\yongqiangcheng\yongqiangcheng\input.txt

input.txt 第一行是测试用例个数 2,后续依次为具体测试用例数据。
测试用例第一行有两个数 YHXWYH = 5 表示迷宫的行数,XW = 4 表示迷宫的列数。
紧接着一行表示起始点坐标 (start_yh, start_xw) 和目标位置坐标 (end_yh, end_xw),注意起始点为 (0, 0)
接下来 YH = 5XW = 4 列为迷宫地图,0 表示空地,1 表示障碍物。

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

2. yongqiang.cpp

/*
============================================================================
Name        : yongqiang.cpp
Author      : Yongqiang Cheng
Version     : Version 1.0.0
Copyright   : Copyright (c) 2020 Yongqiang Cheng
Description : Hello World in C++, Ansi-style
============================================================================
*/#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <time.h>using namespace std;int YH = 0, XW = 0;
int end_yh = 0, end_xw = 0;
int book[50][50] = { 0 };
int min_step = INT_MAX;void dfs(int map_data[50][50], int yh, int xw, int step)
{// right, down, left, up - (height, width)int next[4][2] = { { 0, 1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };int tyh = 0, txw = 0;// 判断是否到达目标位置if ((yh == end_yh) && (xw == end_xw)){if (step < min_step) {min_step = step; // 更新最少步骤}return; // 返回}// 枚举 4 种走法for (int k = 0; k < 4; k++){// 计算下一个点的坐标tyh = yh + next[k][0];txw = xw + next[k][1];// 判断是否越界if ((tyh < 0) || (tyh >= YH) || (txw < 0) || (txw >= XW)) {continue;}// 0 表示空地,1 表示障碍物if ((0 == map_data[tyh][txw]) && (0 == book[tyh][txw])){book[tyh][txw] = 1; // 标记这个点已经走过dfs(map_data, tyh, txw, step + 1); // 开始尝试下一个点book[tyh][txw] = 0; // 尝试结束,取消这个点的标记}}return;
}int inference(int map_data[50][50], int start_yh, int start_xw)
{int ret = 0;// 从起点开始搜索book[start_yh][start_xw] = 1; // 标记起点已经在路径中,防止后面重复走// 第 4 个参数是初始步数 0dfs(map_data, start_yh, start_xw, 0);book[start_yh][start_xw] = 0;ret = min_step;return ret;
}int main()
{clock_t start = 0, end = 0;double cpu_time_used = 0;const char input_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\input.txt";const char output_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\output.txt";freopen(input_txt, "r", stdin);// freopen(output_txt, "w", stdout);start = clock();printf("Start of the program, start = %ld\n", start);printf("Start of the program, start = %ld\n\n", start);int case_num = 0;cin >> case_num;cout << case_num << endl;for (int idx = 1; idx <= case_num; idx++){int start_yh = 0, start_xw = 0;// (height, weight)int map_data[50][50] = { 0 };cin >> YH >> XW; // initializationcin >> start_yh >> start_xw >> end_yh >> end_xw; // initializationcout << YH << XW << endl;cout << start_yh << start_xw << end_yh << end_xw << endl;min_step = INT_MAX; // initializationfor (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cin >> map_data[h][w];}}for (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cout << map_data[h][w];}cout << endl;}inference(map_data, start_yh, start_xw);cout << "CASE #" << idx << " = " << min_step << endl;}end = clock();printf("\nEnd of the program, end = %ld\n", end);printf("End of the program, end = %ld\n", end);cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;printf("Total time taken by CPU: %f\n", cpu_time_used);printf("Exiting of the program...\n");fclose(stdin);// fclose(stdout);return 0;
}

/*
============================================================================
Name        : yongqiang.cpp
Author      : Yongqiang Cheng
Version     : Version 1.0.0
Copyright   : Copyright (c) 2020 Yongqiang Cheng
Description : Hello World in C++, Ansi-style
============================================================================
*/#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <time.h>using namespace std;int YH = 0, XW = 0;
int end_yh = 0, end_xw = 0;
int book[50][50] = { 0 };
int min_step = INT_MAX;void dfs(int map_data[50][50], int yh, int xw, int step)
{// right, down, left, up - (height, width)int next[4][2] = { { 0, 1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };int tyh = 0, txw = 0;// 判断是否到达目标位置if ((yh == end_yh) && (xw == end_xw)){if (step < min_step) {min_step = step; // 更新最少步骤}return; // 返回}// 枚举 4 种走法for (int k = 0; k < 4; k++){// 计算下一个点的坐标tyh = yh + next[k][0];txw = xw + next[k][1];// 判断是否越界if ((tyh < 0) || (tyh >= YH) || (txw < 0) || (txw >= XW)) {continue;}// 0 表示空地,1 表示障碍物if ((0 == map_data[tyh][txw]) && (0 == book[tyh][txw])){book[tyh][txw] = 1; // 标记这个点已经走过dfs(map_data, tyh, txw, step + 1); // 开始尝试下一个点book[tyh][txw] = 0; // 尝试结束,取消这个点的标记}}return;
}int inference(int map_data[50][50], int start_yh, int start_xw)
{int ret = 0;// 从起点开始搜索book[start_yh][start_xw] = 1; // 标记起点已经在路径中,防止后面重复走// 第 4 个参数是初始步数 0dfs(map_data, start_yh, start_xw, 0);book[start_yh][start_xw] = 0;ret = min_step;return ret;
}int main()
{clock_t start = 0, end = 0;double cpu_time_used = 0;const char input_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\input.txt";const char output_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\output.txt";freopen(input_txt, "r", stdin);freopen(output_txt, "w", stdout);start = clock();printf("Start of the program, start = %ld\n", start);printf("Start of the program, start = %ld\n\n", start);int case_num = 0;cin >> case_num;cout << case_num << endl;for (int idx = 1; idx <= case_num; idx++){int start_yh = 0, start_xw = 0;// (height, weight)int map_data[50][50] = { 0 };cin >> YH >> XW; // initializationcin >> start_yh >> start_xw >> end_yh >> end_xw; // initializationcout << YH << XW << endl;cout << start_yh << start_xw << end_yh << end_xw << endl;min_step = INT_MAX; // initializationfor (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cin >> map_data[h][w];}}for (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cout << map_data[h][w];}cout << endl;}inference(map_data, start_yh, start_xw);cout << "CASE #" << idx << " = " << min_step << endl;}end = clock();printf("\nEnd of the program, end = %ld\n", end);printf("End of the program, end = %ld\n", end);cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;printf("Total time taken by CPU: %f\n", cpu_time_used);printf("Exiting of the program...\n");fclose(stdin);fclose(stdout);return 0;
}

2.1 D:\visual_studio_workspace\yongqiangcheng\yongqiangcheng\output.txt

Start of the program, start = 0
Start of the program, start = 02
54
0032
0010
0000
0010
0100
0001
CASE #1 = 7
54
0032
0010
0000
0010
0100
0001
CASE #2 = 7End of the program, end = 1
End of the program, end = 1
Total time taken by CPU: 0.001000
Exiting of the program...

发明深度优先算法的是 John E.Hopcroft 和 Robert E.Tarjan。1971 ~ 1972 年,他们在斯坦福大学研究图的连通性 (任意两点是否可以相互到达) 和平面性 (图中所有的边相互不交叉。在电路板上设计布线的时候,要求线与线不能交叉,这就是平面性的一个实际应用),发明了这个算法。他们也因此获得了1986年的图灵奖。

当我们遇到这种需要分身,需要不断尝试完成的事情时,可以尝试使用深度优先搜索算法,要借助计算机的运算速度优势,完成一些生活中比较繁琐重复的事情。

3. 注意全局变量初始化

上面 2. 的代码用了太多全局变量是不合适,在运行多个测试用例时,切记不可忘记对全局变量的初始化。

/*
============================================================================
Name        : yongqiang.cpp
Author      : Yongqiang Cheng
Version     : Version 1.0.0
Copyright   : Copyright (c) 2020 Yongqiang Cheng
Description : Hello World in C++, Ansi-style
============================================================================
*/#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <time.h>using namespace std;int end_yh = 0, end_xw = 0;void dfs(int map_data[50][50], int yh, int xw, int step, int book[50][50], int YH, int XW, int *min_step)
{// right, down, left, up - (height, width)int next[4][2] = { { 0, 1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };int tyh = 0, txw = 0;// 判断是否到达目标位置if ((yh == end_yh) && (xw == end_xw)){if (step < *min_step) {*min_step = step; // 更新最少步骤}return; // 返回}// 枚举 4 种走法for (int k = 0; k < 4; k++){// 计算下一个点的坐标tyh = yh + next[k][0];txw = xw + next[k][1];// 判断是否越界if ((tyh < 0) || (tyh >= YH) || (txw < 0) || (txw >= XW)) {continue;}// 0 表示空地,1 表示障碍物if ((0 == map_data[tyh][txw]) && (0 == book[tyh][txw])){book[tyh][txw] = 1; // 标记这个点已经走过dfs(map_data, tyh, txw, step + 1, book, YH, XW, min_step); // 开始尝试下一个点book[tyh][txw] = 0; // 尝试结束,取消这个点的标记}}return;
}int inference(int map_data[50][50], int start_yh, int start_xw, int YH, int XW)
{int ret = 0;int book[50][50] = { 0 };int min_step = INT_MAX;// 从起点开始搜索book[start_yh][start_xw] = 1; // 标记起点已经在路径中,防止后面重复走// 第 4 个参数是初始步数 0dfs(map_data, start_yh, start_xw, 0, book, YH, XW, &min_step);book[start_yh][start_xw] = 0;ret = min_step;return ret;
}int main()
{clock_t start = 0, end = 0;double cpu_time_used = 0;const char input_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\input.txt";const char output_txt[] = "D:\\visual_studio_workspace\\yongqiangcheng\\yongqiangcheng\\output.txt";freopen(input_txt, "r", stdin);freopen(output_txt, "w", stdout);start = clock();printf("Start of the program, start = %ld\n", start);printf("Start of the program, start = %ld\n\n", start);int case_num = 0;cin >> case_num;cout << case_num << endl;for (int idx = 1; idx <= case_num; idx++){int start_yh = 0, start_xw = 0;int YH = 0, XW = 0;int ret = 0;// (height, weight)int map_data[50][50] = { 0 };cin >> YH >> XW; // initializationcin >> start_yh >> start_xw >> end_yh >> end_xw; // initializationcout << YH << XW << endl;cout << start_yh << start_xw << end_yh << end_xw << endl;for (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cin >> map_data[h][w];}}for (int h = 0; h < YH; h++){for (int w = 0; w < XW; w++){cout << map_data[h][w];}cout << endl;}ret = inference(map_data, start_yh, start_xw, YH, XW);cout << "CASE #" << idx << " = " << ret << endl;}end = clock();printf("\nEnd of the program, end = %ld\n", end);printf("End of the program, end = %ld\n", end);cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;printf("Total time taken by CPU: %f\n", cpu_time_used);printf("Exiting of the program...\n");fclose(stdin);fclose(stdout);return 0;
}

3.1 D:\visual_studio_workspace\yongqiangcheng\yongqiangcheng\output.txt

Start of the program, start = 113
Start of the program, start = 1132
54
0032
0010
0000
0010
0100
0001
CASE #1 = 7
54
0032
0010
0000
0010
0100
0001
CASE #2 = 7End of the program, end = 114
End of the program, end = 114
Total time taken by CPU: 0.001000
Exiting of the program...

References

《啊哈!算法》 - 啊哈磊

深度优先搜索 - 最短路径相关推荐

  1. 【数据结构与算法】2.深度优先搜索DFS、广度优先搜索BFS

    原文链接:https://blog.csdn.net/qq_41681241/article/details/81432634 总结 一般来说,广搜常用于找单一的最短路线,或者是规模小的路径搜索,它的 ...

  2. 一文搞懂深度优先搜索、广度优先搜索(dfs、bfs)

    前言 你问一个人听过哪些算法,那么深度优先搜索(dfs)和宽度优先搜索(bfs)那肯定在其中,很多小老弟学会dfs和bfs就觉得好像懂算法了,无所不能,确实如此,学会dfs和bfs暴力搜索枚举确实利用 ...

  3. 数据结构与算法—图论之dfs、bfs(深度优先搜索、宽度优先搜索)

    文章目录 前言 邻接矩阵和邻接表 深度优先搜索(dfs) 宽度(广度)优先搜索(bfs) 总结与比较 前言 在有向图和无向图中,如果节点之间无权值或者权值相等,那么dfs和bfs时常出现在日常算法中. ...

  4. 图的广度优先搜索(bfs)以及深度优先搜索(dfs)

    1.前言 和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次. 但是图的遍历相对树而言要更为复杂.因为图中的任意顶点都可能与其他顶点相邻,所以在图的遍历中 ...

  5. LQ训练营(C++)学习笔记_深度优先搜索

    深度优先搜索 三.深度优先搜索 1.普通深度优先搜索 1.1 迷宫问题描述 1.2 代码实现 2.抽象深度优先搜索问题 2.1 和为K问题 2.1.1 问题描述 2.1.2 解题思路 2.1.3 代码 ...

  6. 算法导论--广度优先搜索和深度优先搜索

    广度优先搜索 在给定图G=(V,E)和一个特定的源顶点s的情况下,广度优先搜索系统地探索G中的边,以期"发现"可从s 到达的所有顶点,并计算s 到所有这些可达顶点之间的距离(即最少 ...

  7. 数据结构与算法--图论-深度优先搜索及其应用

    深度优先搜索 深度优先搜索(depth-first search) 是对先序遍历(preorder traversal)的推广,我们从某个顶点v开始处理v,然后递归的遍历所有与v邻接顶点.如果这个过程 ...

  8. ReviewForJob——深度优先搜索的应用

    [0]README 1)本文旨在 介绍 ReviewForJob--深度优先搜索的应用 及其 源码实现 : 2)搜索树的技术分为广度优先搜索 和 深度优先搜索:而广度优先搜索,我们前面利用 广度优先搜 ...

  9. 广度优先搜索(BFS)与深度优先搜索(DFS)

    一.广度优先搜索(BFS) 1.二叉树代码 # 实现一个二叉树 class TreeNode:def __init__(self, x):self.val = xself.left = Nonesel ...

  10. 深度优先搜索及广度优先搜索

    一.深度优先搜索. 1.主要思想:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点走到未被访问顶点,当没有未被访问顶点时,返回上一个顶点,继续访问别的顶点,直到所有的顶点都被访问过. 2:主要通过循 ...

最新文章

  1. 高通平台android7.1系统显示旋转180度
  2. Python中常用的高阶函数
  3. jquery load 事件用法
  4. 凌晨一点的粤海街道对抗来自美国的力量|湾区人工智能
  5. vscode中vue代码高亮_Vue中添加友盟代码统计
  6. 教学转用计算机,一种计算机教学用旋转显示屏的制作方法
  7. 批标准化(batch normalization)与层标准化(layer normalization)比较
  8. 3.1 RNN 循环神经网络 概述(上篇)
  9. 阿里双十一技术揭秘——双十一 手淘技术用了这几招
  10. 中小型企业无线网络设计
  11. 利用python批量创建.xsh文件实现Xshell批量登录服务器
  12. 20200425-读后感-第五项修炼1-系统思考
  13. 【MATLAB】在MATLAB中用梯形法求一个表达式在某区间里的积分值
  14. 如何优雅地处理过期订单
  15. 工地安全帽反光衣识别系统 yolov5
  16. 一个C类地址192.168.1.0划分5个子网,每个子网至少要容纳30台主机,如何规划?...
  17. verilog中一文搞懂有限状态机(FSM)Mealy和Moore状态机(及一段式,二段式,三段式)
  18. 每天学一点英文:Espresso 20210831
  19. 使用pypinyin将中文转换成不带声调的拼音
  20. 使用第三方包mongoose来操作MongoDB数据库,解决报错:MongooseError

热门文章

  1. “微音乐”微信小程序实战开发过程
  2. 微信小程序 #项目笔记# | 从0到1实现婚礼邀请函小程序
  3. 热带鱼水族箱屏幕保护程序
  4. 微信功能栏Android代码,微信隐藏代码合集 你不知道的微信隐藏代码功能
  5. 基于JAVA_JSP电子书下载系统
  6. 2022年ADB 命令知多少?一文2000字详细 ADB 命令大全来啦
  7. Linux 从查看本机地址到端口映射
  8. matlab中的低通freqs,matlab数字低通滤波器 MATLAB一阶低通滤波器的设计
  9. 【Java毕设项目】二十项毕设项目(附源码课件)
  10. 程序员所需要的数学知识都这里了