深度优先搜索/广度优先搜索(解决小哈)

问题省略…

深度优先搜索 (实例:解救小哈)

思路:让小哼往右边走,直到走不通的时候再回到这里,再去尝试另一个方向。规定一个顺序,按顺时针方向来尝试(即按照右、下、左、上的顺序去尝试)。


检查小哼是否已经到达小哈的位置,如果没有到达则找出下一步可以走的地方。为了解决这个问题,此处dfs()函数只需要维护3个参数,分别是x坐标、y坐标、以及当前已经走过的步数step。dfs函数定义如下:

void dfs(int x,int y,int step) {
 return;
}
判断是否已经到达小哈位置,只需要判断当前的坐标和小哈的坐标是否相等即可,如果相等则表明已经到达小哈的位置,如下:

void dfs(int x,int y,int step) {
	if(x==p && y==q) {
	 //更新最小值 
	 if(step<minn)
	    minn=step;
   	    return ;//注意此处返回很重要 
	}		
 	return;
}

如果没有找到小哈位置,则找出下一步可以走的地方。因为有四个方向可以走,根据顺时针方向来尝试(即按照右下左上顺序尝试),定义一个next方向数组,如下:

int next[4][2]={
		{0,1},//向右 
		{1,0},//向下 
		{0,-1},//向左 
		{-1,0}};//向上 
};
通过这个方向数组,使用循环就很容易获得下一步的坐标。此处将下一步的横坐标用tx存储,左边用ty存储。

for(int k=0;k<=3;k++) {
	tx=x+next[k][0];
	ty=y+next[k][1];
}
接下来要对tx、ty进行一些判断。包括是否越界、是否为障碍物,以及这个点是否已经在路径中(避免重复访问一个点)。需要用book[tx][ty]来记录格子(tx,ty)是否已经在路径中。
如果这个点符合所有要求,就对这个点进行下一步的扩展,即dfs(tx,ty,step+1).注意,此处的step+1,因为一旦从这个点开始继续往下尝试,意味着你的步数已经增加1.

for(int k=0;k<=3;k++) {
			tx=x+next[k][0];
			ty=y+next[k][1];
			
			if(tx<1 || tx>n || ty<1 ||ty >m)
				continue;
			
			if(a[tx][ty]==0 && book[tx][ty]==0) {
				book[tx][ty]=1;
				dfs(tx,ty,step+1);
				book[tx][ty]=0;
			}
		} 


完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
int a[51][51],book[51][51];
int n,m,p,q,minn=99999999;
void dfs(int x,int y,int step) {
		//定义一个用于表示走的方向的数组 
		int next[4][2]={
						{0,1},//向右 
						{1,0},//向下 
						{0,-1},//向左 
						{-1,0}};//向上 	
						
		//判断是否到达小哈位置 
		if(x==p && y==q) {
			//更新最小值 
			if(step<minn)
				minn=step;
			return ;//注意此处返回很重要 
		}				
		
		int tx,ty;
		for(int k=0;k<=3;k++) {//枚举4种走法,计算下一个点的坐标 
			tx=x+next[k][0];
			ty=y+next[k][1];
			
			if(tx<1 || tx>n || ty<1 ||ty >m)//判断是否越界 
				continue;
			
			if(a[tx][ty]==0 && book[tx][ty]==0) {//判断该点是否为障碍物或已经在路径中 
				book[tx][ty]=1;
				dfs(tx,ty,step+1);
				book[tx][ty]=0;
			}
		} 
		return ;
	}
	
int main() {
	cin>>n>>m;//读入n行,m列
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];//读入迷宫
			
	int startx,starty;
	cin>>startx>>starty>>p>>q; //读入起点、终点坐标 
	//从起点开始搜索 
	book[startx][starty]=1;	//标记起点已经在路径中,防止后面重复走 
	dfs(startx,starty,0);//第一个参数是起点横坐标,第二个参数是起点纵坐标,第三个参数是初始步数为0
	cout<<minn;
	getchar();getchar();
	return 0; 
	
}
测试数据:

5 4

0 0 1 0

0 0 0 0

0 0 1 0

0 1 0 0
0 0 0 1

1 1 4 3



广度优先搜索 (实例:解救小哈)

思路:通过一层一层拓展方法来找到小哈,拓展时每发现一个点就将这个点加入到队列中,直至走到小哈的位置(p,q)时为止。


用一个结构体来实现队列。

struct note {
	int x;
	int y;
	int s;
};
	struct note que[2501];
        int head,tail;
	int a[51][51]={0};//用来存储地图
        int book[51][51]={0};//作用是记录哪些点已经队列中,防止一个点被重复拓展,全部初始化为0
//最开始需要进行初始化,即将队列设置为空
        head=1;
        tail=1;
        que[tail].x=1;
	que[tail].y=1;
	que[tail].s=0;
        tail++;
        book[1][1]=1;
}

然后从(1,1)开始,先尝试往右走到达(1,2)。

tx=que[head].x;
ty=que[head].y+1;
需要判断(1,2)是否越界。

//判断是否越界 
if(tx<1 || tx>n || ty<1 || ty>m)
	continue;
再判断(1,2)是否为障碍物或者已经在路径中

//判断是否为障碍物 或者已经在路径中 
if(a[tx][ty]==0 && book[tx][ty]==0) {
}

若满足上述两个条件,则将(1,2)入队,并标记该点已经走过。

book[tx][ty]=1;	//把这个点标记为走过,注意宽搜每个点只入列一次,所以和深搜不同,不需要将book数组还原 
//插入新的点到队列中 
que[tail].x=tx;
que[tail].y=ty;
que[tail].s=que[head].s+1;
tail++;

接下来还有继续尝试其他方向走。此处规定一个顺序,即按照顺时针方向来尝试(也就是以右下左上顺序尝试)。发现从(1,1)还是可以到达(2,1),因此 也需要将(2,1)也加入队列,实现代码与刚才一样。
对(1,1)拓展完毕后,(1,1)对我们已经没有用,此时将(1,1)出队。出队操作,即:
head++;

接下来需要在新拓展出的(1,2)和(2,1)这两点的基础上继续向下探索。到目前为止已经拓展出从起点出发一步以内可以到达的所有点。因为还没有到达小哈的位置,所以继续。直至走到小哈位置,算法结束、


完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
struct note {
	int x;
	int y;
	int f;//父亲在队列中的编号,本体不要求输出路径,可以不需要f 
	int s;
};
int main() {
	struct note que[2501];
	int a[51][51]={0},book[51][51]={0};
	
	//定义一个用于表示走的方向的数组 
	int next[4][2]={
					{0,1},//向右 
					{1,0},//向下 
					{0,-1},//向左 
					{-1,0}};//向上 
	
	int k,n,m,tx,ty,flag;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
			
	int startx,starty,p,q;
	cin>>startx>>starty>>p>>q; 
	
	//队列初始化 
	int head,tail; 
	head=1;
	tail=1;
	//往队列插入迷宫入口坐标 
	que[tail].x=startx;
	que[tail].y=starty;
	que[tail].f=0;
	que[tail].s=0;
	tail++;
	book[startx][starty]=1;
	
	flag=0;//用来标记是否到达目标点,0表示暂时没有到达,1表示到达
	
	while(head<tail) {	//当队列不空时候循环 
		for(int k=0;k<=3;k++) {
			//计算下一点坐标
			tx=que[head].x+next[k][0];
			ty=que[head].y+next[k][1];
			
			//判断是否越界 
			if(tx<1 || tx>n || ty<1 || ty>m)
				continue;
			
			//判断是否为障碍物 或者已经在路径中 
			if(a[tx][ty]==0 && book[tx][ty]==0) {
				book[tx][ty]=1;	//把这个点标记为走过,注意宽搜每个点只入列一次,所以和深搜不同,不需要将book数组还原 
				//插入新的点到队列中 
				que[tail].x=tx;
				que[tail].y=ty;
				que[tail].f=head;//因为这个点是从head拓展而来,所以它的父亲是head,本题目不需要求路径,因此本句可以省略 
				que[tail].s=que[head].s+1;
				tail++;
			} 
			
			if(tx==p &&ty==q) {
				flag=1;
				break;
			} 
		}
		if(flag==1)
			break;
		head++;//此地方不能忘记,当一个点拓展结束,head++才能对后面的点再进行拓展 
	} 
	
	//打印队列中末尾最后一个点(目标点)的步数
	//注意tail是指向队列队尾(即最后一位)的下一个位置,所以这需要-1 
	printf("%d",que[tail-1].s);
	getchar();getchar();
	return 0; 
	
}

节选自《啊哈,算法》这一本书,作者啊哈 用通俗易懂的语言把2个算法思想较为简单的表达出来。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值