12032 解救小哈

本文介绍了解救迷宫中小哈的问题,使用广度优先搜索算法找到从起点到终点的最短路径,并对比了深度优先搜索算法的优缺点。
标题:

解救小哈

标签:搜索 广度优先搜索
详情:  有一天,小哈一个去玩迷宫。但是方向感很不好的小哈很快就迷路了。小哼得知后便立即去解救无助的小哈。小哼当然是有备而来,已经弄清楚了迷宫地图,现在小哼要以最快速度去解救小哈。问题就此开始了……
  迷宫由n行m列的单元格组成,每个单元格要么是空地,要么是障碍物。你的任务是帮助小哼找到一条从迷宫的起点到小哈所在位置的最短路径,注意障碍物是不能走的,当然也不能走到迷宫之外。n和m都小于等于100。
输入格式:
第一行有两个数N M。N表示迷宫的行,M表示迷宫的列。接来下来N行M列为迷宫,0表示空地,1表示障碍物。最后一行4个数,前两个数为迷宫入口的x和y坐标。后两个为小哈的x和y坐标。
输出格式:
一个整数表示小哼到小哈的最短步数。如果不能解救小哈则输出No Way!
提示:RQNOJ 34紧急援救
195校园迷宫
样例:

输入

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

输出

7

输入

3 3
1 1 1 
0 1 0 
0 1 0 
2 1 3 3

输出

No Way!

 

做法一:

DFS:就是一直按照一条路径去寻找,找到以后就返回,看看有没有更短的路径,知道把所有的路径都搜索完了就结束,打印出最终的结果。我们需要用一个二维数组来存储这个迷宫,当小哼站在起点时他有四个方向可以走,右,下,左,上,我们可以一个二维数组来存储这四个方向,然后依次枚举这四个方向。由于在找路径的过程中走了很多重复地点,所以在走的过程中还需要一个数组来标记这个点有没有被走过,走过就标记为1,当返回时,在标记为0,这就相当于一个栈,把能走的点先放到栈中,先尝试右边的点,再把后面能走的点放入栈中,重复这样的过程,最后就能找出所有的路径。

代码(TLE):

#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
int a[101][101],mark[101][101];
int n,m,ex,ey,mmin=9999999;
void dfs(int x,int y,int step)
{
	int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//右、下、左、上
	if(x==ex&&y==ey)//看是否到达终点
	{
		if(step<mmin)
			mmin=step;
		return;//返回不可缺少
	}
	int nx,ny;
	for(int k=0;k<4;k++)//枚举四种方向
	{
		nx=x+next[k][0];
		ny=y+next[k][1];
		if(nx<1||nx>n||ny<1||ny>m)//检查是否越界
			continue;
		if(a[nx][ny]==0&&mark[nx][ny]==0)//下一个点是否是障碍物或者是否已经走过
		{
			mark[nx][ny]=1;
			dfs(nx,ny,step+1);//继续下一个点
			mark[nx][ny]=0;//尝试结束后,取消这个点的标记,方便下一次的尝试
		}
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    }
	int sx,sy;
	scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
	mark[sx][sy]=1;//标记第一个点
	dfs(sx,sy,0);
    //输出最小步数
	if(mmin==9999999)
		printf("No Way!\n");
	else
		printf("%d\n",mmin);
	return 0;
}

对于本题的数据来说,这个方法超时了,为什么呢,我觉得是因为这个走了很多重复的点,由于用回溯的方法,有些点可能被走了很多次。所以造成了超时。下面我们来看另外一种方法。

做法二:

我们按照一层一层的方法来寻找终点,当小哼在一个点上的时候,我们把他所有可能走到的点全部放到队列中去,在队列中的点,我们每拿出一个,就把他所有可能走的点放在队尾。为了不出现DFS一个点走好多遍的情况,我们可以用一个数组来标记一个点有没有走过。我们不断的去尝试每个点可能得到的结果。

AC代码:

//一个有bug却ac的代码
#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
//结构体模拟一个队列,队列中有坐标和步数
struct A
{
	int x,y,step;	
};
int main()
{
	struct A q[10001];
	int n,m;
	scanf("%d%d",&n,&m);
	int a[101][101],mark[101][101]={0};
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	int sx,sy,ex,ey;
	scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
	int head=1,tail=1;//队首,队尾的下一个位置 
	q[tail].x=sx;//把第一个点加入到队列中
	q[tail].y=sy;
	q[tail].step=0;
	tail++;
	mark[sx][sy]=1;//标记第一个点
	int flag=0;//标记是否到达目标点
	int next[4][2]={0,1,1,0,0,-1,-1,0}; //四个方向
	while(head<tail)//head==tail时,队列为空 
	{
		for(int k=0;k<4;k++)//四种走法
		{
			int nextx=q[head].x+next[k][0];
			int nexty=q[head].y+next[k][1];
			if(nextx<1||nextx>n||nexty<1||nexty>m)//检查是否越界
				continue;
			if(a[nextx][nexty]==0&&mark[nextx][nexty]==0)
			{
				mark[nextx][nexty]=1;//每个点只入队一次,不用还原。
				q[tail].x=nextx;//经这个点入队
				q[tail].y=nexty;
				q[tail].step=q[head].step+1;//这个点是由队首那个点走过来的 
				tail++; 
			}
			if(nextx==ex&&nexty==ey)//到达终点
			{
				flag=1;
				break;
			}
		}
		if(flag==1)//如果找到路径直接跳出打印步数(第一次找到的路径是最短的)
			break; 
		head++;//继续拿出下一个点去扩展 
	}
	if(flag==1)
		printf("%d\n",q[tail-1].step); //tail表示的是队尾的下一个点
	else//到不了目标点
		printf("No Way!\n");
	return 0;
} 

这样就避免做很多重复地点了,每个点只走了一次。

//正解
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string> 
#define pi 3.1415926
#define mod 1000000007
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long LL;
typedef pair<int,int> Node;
const int Max_n=105;
int a[Max_n][Max_n];
int net[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int n,m;
int stratx,straty,endx,endy;

int bfs(){
	int step[Max_n][Max_n];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			step[i][j]=100000;
	queue<Node>q;
	q.push(Node(stratx,straty));
	step[stratx][straty]=0;
	while(q.size()){
		Node node=q.front();q.pop();
		if(node.first==endx&&node.second==endy) break;
		for(int i=0;i<4;i++){//枚举四个方向(ps:此处从0开始) 
			int tx=node.first+net[i][0];
			int ty=node.second+net[i][1];
			//判断是否越界
			if(tx<1||tx>n||ty<1||ty>m) continue;
			if(a[tx][ty]==0&&step[tx][ty]==100000){//这个点可以走并且没有走过 
				q.push(Node(tx,ty));
				step[tx][ty]=step[node.first][node.second]+1;
			} 
		}
	}
	return step[endx][endy];//返回最后一个点的步数 
}
int main(){ 
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	scanf("%d%d%d%d",&stratx,&straty,&endx,&endy);
	int step=bfs();
	if(step==100000) printf("No Way!\n");
	else printf("%d\n",step);
	return 0;
}
//一个有bug,却ac的代码
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string> 
#define pi 3.1415926
#define mod 1000000007
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
typedef pair<pair<int,int>,int> Node;
const int Max_n=105;
int a[Max_n][Max_n],vis[Max_n][Max_n];
int net[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
queue<Node>q;
int n,m;
int stratx,straty,endx,endy;

int bfs(){
	int flag=0;
	while(!q.empty()){
		for(int i=0;i<4;i++){//枚举四个方向(ps:此处从0开始) 
			int tx=q.front().first.first+net[i][0];
			int ty=q.front().first.second+net[i][1];
			//判断是否越界
			if(tx<1||tx>n||ty<1||ty>m) continue;
			if(a[tx][ty]==0&&vis[tx][ty]==0){//这个点可以走并且没有走过 
				vis[tx][ty]=1;//标记已经走过
				q.push({{tx,ty},q.front().second+1});
				//q.front().second.second当走的都是前这个点延申出去的 
			} 
			if(tx==endx&&ty==endy){//到达目标点之后,退出循环 
				flag=1;
				break; 
			}	 
		}
		if(flag) break;//到达目标点 
		q.pop();
		//这里不要忘记,一个点的所有情况都拓展完了以后删除队首元素,否则是死循环 
	}
	return  flag;//返回最后一个点的步数 
}
int main(){ 
	memset(vis,0,sizeof(vis));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	scanf("%d%d%d%d",&stratx,&straty,&endx,&endy);
	q.push({{stratx,straty},0});
	vis[stratx][straty]=1;
	int flag=bfs();
	if(flag) printf("%d\n",q.back().second);
	else printf("No Way!\n");
	return 0;
}

 

转载于:https://www.cnblogs.com/zut-syp/p/10543722.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值