12/15 从迷宫问题看DFS、BFS

这篇博客介绍了如何使用深度优先搜索(DFS)和广度优先搜索(BFS)解决迷宫问题。作者首先解释了DFS的基本思想,即一条路走到黑,无路可走时回溯,然后详细阐述了实现DFS的步骤,并讨论了使用STL容器queue的优势。尽管最初打算使用DFS,但作者最终选择了BFS,因为BFS能确保找到唯一解。通过BFS,作者从右下角开始反向搜索,记录每个节点的步数,最后给出了实现BFS的代码示例。

说白了,深搜就是递归的加强版

优先考虑深度,换句话说就是一条路走到黑,直到无路可走的情况下,才会选择回头,然后重新选择一条路

先看看迷宫问题的题目

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};

      它表示一个迷宫,其中的1表示墙壁0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

答案一眼就能看出来是8,但是要计算机去试探、

  

1、一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

2、二维数组,起点为a[0][0]

3、在起点的那个人可以往上下左右试探,有一个顺序,可以顺时针,先往右边然后顺时针

4、每走过一个点,都要把那个点设置为已访问,也就是在路上做标记

5、到达下一个点的时候,和起点一样,都要向四个方向试探,如果无路可走则不考虑这个方向

6、第一步和第二步的时候都只能往下一个方向移动,到第三个点位的时候就多了选择,可以向右走也可以向下走,这边先向右走,往终点的方向走,形成第一条路线,路上以访问点的数量就是步数,这只是其中的一条路径,不一定是最短的                                    

7、试探其他的路径,因此需要回溯,回溯是什么意思呢,并不是瞬移回到起点,而是从刚刚到达的终点往回走,此时路上还有做过的标记,走完一个点,就要把标记给摘掉,这道题没什么死路,如果有遇到死路,就要回退到路口处重新选择,标记不一定要全部摘掉,这些标记可以留下来当作下一条路径的标记

既然学了DFS,为何不用万能的STL容器去存呢

在做出来之前,先扩展一下,C++的STL容器queue

里面有如下可以被调用的函数

  1. empty(); 如果队列空则返回真
  2. push(          (这个里面加入你需要加入的元素或者结构体)         ); 在末尾加入一个元素
  3. front(); 返回第一个元素
  4. back();返回最后一个元素
  5. pop(); 删除第一个元素
  6. size();返回队列中元素的个数

定义一个队列之前,需要明确要定义什么队列,例如,结构体

queue<(结构体的名字)> (你定义的队列名字);

queue<node> M;

定义一个队列之后最好看一下这个队列是不是空的

队列函数使用样例

#include<iostream>

#include<queue>

using namespace std;

struct hou

{

int x,y;

char c;

};

int main()

{

hou m;

queue<hou > M;

if(!M.empty()) cout<<"完了,没地方存了";

for(int i=1;i<=100;i+=3)

{

m.x =i;

m.y =i+100;

m.c =i;

M.push(m);

cout<<"**"<<M.size()<<"**"<<endl;

}

while(!M.empty())

{

hou n;//用来记录读出来的东西;

n=M.front();

cout<<n.x<<'*'<<n.y<<'*'<<n.c<<'*'<<endl;

M.pop();

}

return 0;        

}

这段代码是一个教程转来的

最后我还是用了BFS来写,没想到吧

广度搜索的好处就是能够找出来唯一解

先从最右下到左上用BFS遍历一遍,记录好到达每个节点的步数。

然后反过来,从左上开始搜索。

每次搜索之前记录的步数比当前节点步数少一的点,符合条件的便是要输出的点

废话不多说,直接上代码

#include<iostream>
#include<queue>//队列函数库
#include<string.h>
using namespace std;
int main()
{
	queue<int>q;//定义一个int类型的队列q
	int i,j,x,y;
	int dx[4]={0,1,-1,0};//左右方向定义数组
    int dy[4]={1,0,0,-1};//上下方向定义数组
    int vis[5][5],map[5][5];
	char ch[5][5];

	for (i=0;i<5;i++)
		for (j=0;j<5;j++)
			cin>>ch[i][j];
		//迷宫以二维字符数组的形式输入
	    int vx=0,vy=0;
		q.push(4);//push的括号里面加所需要的元素,这里是4
		q.push(4);//push的意思是在队列的末尾加上一个元素
		memset(vis,0,sizeof(vis));
		//memset函数使用方法
		//memset(数组,赋给数组的值,数组的长度)
		//数组的长度用sizeof函数计算
		//这里的意思是这个二维数组全部赋初值0
		//一般情况下,memset对于结构体清零比较有效

		memset(map,0,sizeof(map));//同上
		while (!q.empty())//指的是如果队列一直不为空则一直循环
		{
			vx=q.front();//front的意思是返回第一个元素
			q.pop();//pop前缀是队列q,意思是删除队列q的第一个元素
			vy=q.front();
			q.pop();
			if (vx==0&&vy==0)//当返回的元素删除完的时候停止循环
            break;
		for (i=1;i<4;i++)
		{
		  x=vx+dx[i];
		  y=vy+dy[i];
		  if (!vis[x][y]&&ch[x][y]=='0'&&x>=0&&x<5&&y>=0&&y<5)
		  {
			  q.push(x);
			  q.push(y);
			  vis[x][y]=1;
			  map[x][y]=map[vx][vy]+1;
		  } 
		}
		}
		vx=0;vy=0;//归零
		cout<<"(0, 0)"<<endl;
		int k=0;
	while(k!=map[0][0]-1)
	{
		for (i=0;i<3;i++)
		{
			x=vx+dx[i];
			y=vy+dy[i];
			if (map[x][y]==map[vx][vy]-1) 
			{   
				cout<<'('<<x<<", "<<y<<')'<<endl;//输出烤串,<<是签子
			    k++;
				break;
			}
		}
		vx=x;
		vy=y;
	}
	cout<<"(4, 4)"<<endl;//最后一步终点肯定是(4,4)
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值