解救小哈——DFS算法举例
一、问题引入
有一天,小哈一个人去玩迷宫。但是方向感不好的小哈很快就迷路了。小哼得知后便去解救无助的小哈。此时的小哼已经弄清楚了迷宫的地图,现在小哼要以最快的速度去解救小哈。那么,问题来了...

二、问题的分析
首先我们用一个二维数组来存储这个迷宫,刚开始的时候,小哼处于迷宫的入口处(1,1),小哈在(p,q)。其实这道题的的本质就在于找从(1,1)到(p,q)的最短路径。
此时摆在小哼面前的路有两条,我们可以先让小哼往右边走,直到走不通的时候再回到这里,再去尝试另外一个方向。
在这里我们规定一个顺序,按照顺时针的方向来尝试(即右→下→左→上)。

我们先来看看小哼一步之内可以到达的点有哪些?只有(1,2)和(2,1)。
根据刚才的策略,我们先往右边走,但右边(1,3)有障碍物,所以只能往下(2,2)这个点走。但是小哈并不在(2,2)这个点上,所以小哼还得继续往下走,直至无路可走或者找到小哈为止。
注意:并不是让我们找到小哈此题就解决了。因为刚才只是尝试了一条路的走法,而这条路并不一定是最短的。刚才很多地方在选择方向的时候都有多种选择,因此我们需要返回到这些地方继续尝试往别的方向走,直到把所有可能都尝试一遍,最后输出最短的一条路径。
例如下图就是一条可行的搜索路径:

三、解决问题——深度优先搜索
(1)如何写dfs函数。
dfs函数的功能是解决当前应该怎么办。而小哼处在某个点的时候需要处理的是:先检查小哼是否已经到达小哈的位置,如果没有到达则找出下一步可以走的地方。
为了解决这个问题,此处dfs()函数只需要维护三个参数,分别是当前这个点的x坐标,y坐标以及当前已经走过的步数step。
//dfs函数定义如下:
void dfs(int x,int y,int step)
{
return ;
}
(2)判断是否已经到达小哈的位置。
只需要判断当前的坐标是否与小哈的坐标相等就可以了,如果相等就标明已经到达小哈的位置。
void dfs(int x,int y,int step)
{
if(x==p && y==q) //判断是否到达小哈的位置
{
if(step<min)
min=step; //更新最小值
return; //这步很重要!
}
return ;
}
(3)如何获得下一个方向的坐标(此处定义一个方向数组)。
    int next[][]={
    {,},//向右走
    {,},//向下走
    {,-},//向左走
    {-,},//向上走
    };

通过这个方向数组,使用循环就可以方便地得到下一步的坐标。
这里将下一步的横坐标用tx存储,纵坐标用ty存储。
for(k=;k<=;k++)
{
/*计算下一个点的坐标*/
tx=x+next[k][];
ty=y+next[k][];
}
(4)对下一个点(tx,ty)进行判断(是否越界,是否有障碍物,是否已经在路径中)。
在这里我们用book[tx][ty]来记录格子[tx][ty]是否已经在路径中。
如果这个点符合所有的要求,就对这个点进行下一步的扩展,即dfs(tx,ty,step+1)。
注意这里是step+1,因为一旦从这个点开始继续往下尝试,就意味着步数已经增加了1。
for(k=;k<=;k++)
{
/*计算下一个点的坐标*/
tx=x+next[k][];
ty=y+next[k][];
if(tx< || tx>n || ty< || ty>m) //判断是否越界
continue;
/*判断该点是否为障碍物或者已经在路径中*/
if(a[tx][ty]== && book[tx][ty]==)
{
book[tx][ty]=; //标记这个点已经走过
dfs(tx,ty,step+); //开始尝试下一个点
book[tx][ty]=; //尝试结束,取消这个点的标记
}
}
四、完整代码
#include<stdio.h>
int n,m,p,q,min=;
int a[][],book[][];
void dfs(int x,int y,int step)
{
int next[][]={
{,},//向右走
{,},//向下走
{,-},//向左走
{-,},//向上走
};
int tx,ty,k;
if(x==p && y==q) //判断是否到达小哈的位置
{
if(step<min)
min=step; //更新最小值
return;
}
/*枚举四种走法*/
for(k=;k<=;k++)
{
/*计算下一个点的坐标*/
tx=x+next[k][];
ty=y+next[k][];
if(tx< || tx>n || ty< || ty>m) //判断是否越界
continue;
/*判断该点是否为障碍物或者已经在路径中*/
if(a[tx][ty]== && book[tx][ty]==)
{
book[tx][ty]=; //标记这个点已经走过
dfs(tx,ty,step+); //开始尝试下一个点
book[tx][ty]=; //尝试结束,取消这个点的标记
}
}
return;
} int main()
{
int i,j,startx,starty;
scanf("%d %d",&n,&m); //读入n和m,n为行,m为列
/*读入迷宫*/
for(i=;i<=n;i++)
for(j=;j<=m;j++)
scanf("%d",&a[i][j]);
scanf("%d %d %d %d",&startx,&starty,&p,&q); //读入起点和终点坐标
/*从起点开始搜索*/
book[startx][starty]=; //标记起点已经在路径中,防止后面重复走
dfs(startx,starty,); //第一个参数是起点的x坐标,以此类推是起点的y坐标,初始步数为0
printf("%d",min); //输出最短步数
return ;
}
五、写在最后
发明深度优先算法的是John E.Hopcroft 和 Robert E.Tarjan。他们并不是研究全排列或者迷宫问题时发明了这个算法。
1971~1972年,他们在斯坦福大学研究图的连通性(任意两点是否可以相互到达)和平面性(图中所有的边相互不交叉。在电路板上设计布线的时候,要求线与线不能交叉,这就是平面性的一个实际应用),发明了这个算法。他们也因此获得了1986年的图灵奖。
在授奖仪式上,当年全国象棋程序比赛的优胜者说他的程序用的就是深度优先搜索算法,这是以其制胜的关键。
通过本次学习,我明白了当我们遇到这种需要“分身”,需要不断尝试完成的事情时,可以尝试使用深度优先搜索算法,因为计算机的运算速度还是很强的,我们要借助他的优势,完成一些生活中比较繁琐重复的事情。
(注:文章内容源自 啊哈磊的《啊哈算法》——很有意思的一本算法入门书!)
解救小哈——DFS算法举例的更多相关文章
- 解救小哈——dfs深搜
		
问题描述: 小哈去玩迷宫,结果迷路了,小哼去救小哈.迷宫由n行m列的单元格组成(n和m都小于等于50),每个单元格要么是空地,要么是障碍物. 问题:帮小哼找到一条从迷宫的起点通往小哈所在位置的最短路径 ...
 - 啊哈算法之宽搜BFS解救小哈
		
简述 本算法摘选自啊哈磊所著的<啊哈!算法>第四章第三节的题目——BFS算法再次解救小哈.文中代码使用C语言编写,博主通过阅读和理解,重新由Java代码实现了一遍,以此来理解BFS算法.关 ...
 - BFS/DFS算法介绍与实现(转)
		
广度优先搜索(Breadth-First-Search)和深度优先搜索(Deep-First-Search)是搜索策略中最经常用到的两种方法,特别常用于图的搜索.其中有很多的算法都用到了这两种思想,比 ...
 - 图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))
		
sdut 2140 图结构练习——判断给定图是否存在合法拓扑序列 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 给定一个有向图 ...
 - DFS算法(——模板习题与总结)
		
首先,需要说明的是搜索算法本质上也是枚举的一种,时间复杂度还是很高的,遇到问题(特别是有水平的比赛上),不要优先使用搜索算法. 这里总结一下DFS算法: 1.从图中某个顶点出发,访问v. 2.找出刚访 ...
 - DFS 算法总结
		
DFS 算法总结 这篇文章会对DFS进行一个总结,列举的题目则是从LeetCode上面选的: 适用场景: 有三个方面,分别是输入数据.状态转换图.求解目标: 输入数据:如果是递归数据结构,如单链表,二 ...
 - 2018-02-03-PY3下经典数据集iris的机器学习算法举例-零基础
		
---layout: posttitle: 2018-02-03-PY3下经典数据集iris的机器学习算法举例-零基础key: 20180203tags: 机器学习 ML IRIS python3mo ...
 - UVA 291 The House Of Santa Claus(DFS算法)
		
题意:从 节点1出发,一笔画出 圣诞老人的家(所谓一笔画,就是遍访所有边且每条边仅访问一次). 思路:深度优先搜索(DFS算法) #include<iostream> #include&l ...
 - POJ 3620 Avoid The Lakes(dfs算法)
		
题意:给出一个农田的图,n行m列,再给出k个被淹没的坐标( i , j ).求出其中相连的被淹没的农田的最大范围. 思路:dfs算法 代码: #include<iostream> #inc ...
 
随机推荐
- .NET MVC EF框架数据库连接配置
			
1:数据库的配置和连接 Web.config <connectionStrings> <add name="SQLConnectionString" connec ...
 - GDB 调试解析
			
GDB(GNU Debugger)是一个强大的命令行调试工具.大家知道命令行的强大就是在于,其可以形成执行序 列,形成脚本.UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优 ...
 - angular.js测试框架protracotr安装所需的node版本
			
protractor内代码的语法是基于ES6的,比如:里面用到了展开运算符“...”,node.js 6.0以下是不支持该语法特性. 所以,安装protractor是不会报错,但运行webdriver ...
 - 每日英语:The Benefits of a Sleep Buddy
			
Stolen sheets, snoring and hot flashes are just some of the annoyances that lead a quarter of U.S. c ...
 - ZooKeeper示例 分布式锁
			
[转载请注明作者和原文链接, 如有谬误, 欢迎在评论中指正. ] 场景描述 在分布式应用, 往往存在多个进程提供同一服务. 这些进程有可能在相同的机器上, 也有可能分布在不同的机器上. 如果这些进程 ...
 - Python处理大数据
			
起因 Python处理一下数据,大概有六七个G,然后再存到另外一个文件中,单线程跑起来发现太慢了,数据总量大概是千万行的级别,然后每秒钟只能处理不到20行--遂想怎么提高一下速度 尝试1-multip ...
 - DIV+CSS网页设计规范
			
1.网页HTML代码最前面必须包括W3C声明,以便符合标准: 一般网页: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transition ...
 - 常用的 Linux iptables 规则
			
一些常用的 Linux iptables 规则,请根据自己的具体需要再修改. 转载自:http://mp.weixin.qq.com/s/uAPzh9_D4Qk6a3zBh7Jq5A # 1. 删除所 ...
 - android之滑屏的实现
			
★理论知识 ●View在屏幕上显示出来要先经过measure(计算)和layout(布局). ◆当控件的父元素正要放置该控件时调用,父元素会问子控件“你想要用多大的地方?”,然后传入两个参数widt ...
 - 移动web开发(二)——viewport
			
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scal ...