搜索之走迷宫

用dfs和bfs解决迷宫问题

小明走迷宫

输入

输入多组测试数据。
对于每组数据,第一行是两个数字n和m,表示该阵为nm大小(0<m,n<=10,n为行,m为列)。
接下来是一个mn的矩阵表示这个阵。
其中0为该阵中可走的路,1为墙(不可走),2为小明的位置,3为出口的位置。
小明只有四个可行的行走方向:即上、下、左、右。


输出

如果小明可以逃离,输出Yes,反之输出No


输入样例

8 7
1 1 1 1 1 1 3
1 0 0 0 0 0 0
1 0 1 1 1 1 1
1 0 0 0 1 1 1
1 1 1 0 0 0 1
1 0 0 0 1 0 1
1 0 1 1 1 0 1
2 0 1 1 1 0 0
4 4
0 3 1 2
1 1 1 0
0 0 1 0
0 0 0 0


输出样例

Yes
No


一、用dfs解决

首先解决输入问题,就直接上代码

//关于输入部分的代码
while(scanf("%d%d",&n,&m)!=EOF){//多组测试数据
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
			if(a[i][j]==2){//入口
				sx=i;
				sy=j;
			}
			if(a[i][j]==3){//出口
    				sx=i;
    				sy=j;
   			}
   		}
   	}
}

接着就开始用dfs解决问题,dfs的思路就是朝一个方向一直搜,碰壁后就返回到上一个节点朝另一个方向搜索,直到搜到最后的目的地。因此很容易想到,使用一个变量来判断是否能到终点,然后用dfs的思路进行搜索。


解决一些写dfs函数前的问题

//首先定义一些全局变量与数组进行处理,这里以行增加为x方向增加,列增加为y方向增加
int a[12][12];//存储迷宫
int b[12][12];//存储是否走过迷宫某个位置
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //四个方向坐标变化(右下左上)
int flag=0;//判断是否能到终点

进入正题,dfs函数的完成

//关于dfs函数的代码
void dfs(int x,int y,int p,int q){//(x,y)为当前坐标,(p,q)为终点坐标
	int tx,ty,k;
	if(a[x][y]==3){
		flag=1;//走出迷宫
		return;//返回上一节点(其实可以使函数结束,但很多问题都需要返回
	}
	for(k=0;k<4;k++){//四个方向
		tx=x+next[k][0];//下一个位置的x坐标
		ty=y+next[k][1];//下一个位置的y坐标
		if(tx<1||ty<1||tx>n||ty>m) continue;//超出迷宫范围
		if((a[tx][ty]==0||a[tx][ty]==3)&&b[tx][ty]==0){
			//第一个判断是否能走,第二个判断是否走过
			b[tx][ty]=1;//不能走回头路了
			dfs(tx,ty,p,q);//递归
			b[tx][ty]=0;//要回到这尝试下一个方向,这个点标记取消
		}
	}
	return;//死路,返回上一节点
}

完成了dfs函数的代码后,整合一下就可得到完整的代码了

//完整代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int a[12][12];
int b[12][12];
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; 
int n,m,flag=0;
void dfs(int x,int y,int p,int q)
{
	int tx,ty,k;
 	if(a[x][y]==3){
  		flag=1;
  		return;
	}
 	for(k=0;k<4;k++){
  		tx=x+next[k][0];
  		ty=y+next[k][1];
  		if(tx<1||ty<1||tx>n||ty>m) continue;
  		if((a[tx][ty]==0||a[tx][ty]==3)&&b[tx][ty]==0){
   			b[tx][ty]=1;
   			dfs(tx,ty,p,q);
   			b[tx][ty]=0;
  		}
	}
 	return;
}
int main()
{  
 	int i,j;
 	int sx,sy,ex,ey;
 	for(i=1;i<=11;i++){//把迷宫先全变成有墙(容易理解)
  		for(j=1;j<=11;j++){
   			a[i][j]=1;
  		}
 	}
 	while(scanf("%d%d",&n,&m)!=EOF){
  		for(i=1;i<=n;i++){
   			for(j=1;j<=m;j++){
    				scanf("%d",&a[i][j]);
    				if(a[i][j]==2){
     					sx=i;
     					sy=j;
    				}
    				if(a[i][j]==3){
     					ex=i;
					ey=j;
				}
   			}
  		}
  		b[sx][sy]=1;//起点走过了
  		dfs(sx,sy,ex,ey);
  		if(flag) printf("Yes\n");
  		else printf("No\n");
  		for(i=1;i<=11;i++){
   			for(j=1;j<=11;j++){
    				a[i][j]=1;
    				b[i][j]=0;
   			}
  		}
  		flag=0;
 	}
 	return 0;
}

二、用bfs解决

输入代码部分与准备部分代码同上,bfs的思路是寻找这个位置再走一步能到哪些位置,将这些位置入队列,然后下一个能走到的位置再重复进行这些操作。

直接上代码(重复部分没有标注解释)

#include<stdio.h>
#include<string.h>
int a[12][12];
int b[12][12];
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; 
struct note//定义一个队列
{
 	int x;
 	int y;
};
int main()
{  
 	int i,j,k;
 	int n,m;
 	int sx,sy,ex,ey;
 	int tx,ty;
 	struct note que[120]; 
 	for(i=1;i<=13;i++){
  		for(j=1;j<=11;j++){
   		a[i][j]=1;
  		}
	}
 	while(scanf("%d%d",&n,&m)!=EOF){
  		int head=1,tail=1,flag=0;//定义队列头,队列尾
  		for(i=1;i<=n;i++){
   			for(j=1;j<=m;j++){
	    			scanf("%d",&a[i][j]);
	    			if(a[i][j]==2){
	     				sx=i;
	     				sy=j;
	    			}
	    			if(a[i][j]==3){
	     				ex=i;
	     				ey=j;
	    			}
	   		}
	  	}
  		que[tail].x=sx;//开始横坐标入队
  		que[tail].y=sy;//开始纵坐标入队
  		tail++;
  		b[sx][sy]=1;
  		while(head<tail){//还有某个位置可以继续拓展
   			for(k=0;k<4;k++){
    				tx=que[head].x+next[k][0];
    				ty=que[head].y+next[k][1];
    				if(tx<1||ty<1||tx>n||ty>m) continue;
    				if((a[tx][ty]==0||a[tx][ty]==3)&&b[tx][ty]==0){
	     				b[tx][ty]=1;
	     				que[tail].x=tx;//成立的点入队
	     				que[tail].y=ty;
	     				tail++;//队尾推进
	     				//这里不需要再将这点清零,因为不用回来
	   			}
    				if(a[tx][ty]==3){
     					flag=1;
     					break;
    				}
   			}
   			if(flag==1){
    				break;
   			}
   			head++;
 		}
  		if(flag) printf("Yes\n");
  		else printf("No\n");
  		for(i=0;i<=13;i++){
   			for(j=0;j<=13;j++){
    				a[i][j]=1;
    				b[i][j]=0;
   			}
  		}
  		flag=0;
 	}
 	return 0;
}

至此,结束,欢迎各位指正

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值