用C写BFS相关题的坎坷之路

题目:迷路小青蛙

Description

小青蛙迷路了,你能帮他回家吗?

Input
n m(1<=n<=m<=85)表示地图的大小为n行m列

接下来输入一个n*m的地图

其中,'0’表示无障碍物,可以通过

'#'表示有一个障碍物,无法通过

‘S’表示青蛙的起点

'E’表示青蛙的终点

Output
输出一个数,表示情况可以回家的最短路长度(每移动一次,算一步)

倘若无论如何也回不了家,输出“Sad”(不包括空格)
Sample Input 1
3 3
0S0
00#
00E

Sample Output 1
3

Sample Input 2
3 3
0S0
00#
0#E

Sample Output 2
Sad

最初始版本:DFS

由于做这题之前做过一道排列组合的题,就自然而然地想到了深搜,就用深搜尝试着写了这题。

#include<stdio.h>
char a[90][90];
int n,m;//地图面积
int minway=9999999;   //int minway=0;//记录最短路长度
int book[90][90];//标记是否走过 
int min(int a,int b)//用于比较路线长度 
{
	return a>b?b:a;
}


void dfs(int x,int y,int way)  //x,y为起始点坐标,way为步数 
{
	int tx,ty;
	int i,b[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
//	int book[90][90]={0};//标记是否走过    //必须定义为全局变量 
	if(a[x][y]=='E')//递归出口 
	{
		minway=min(minway,way);
		return;
	}
	//else
	//{//四种方向
		for(i=0;i<4;i++)
		{
			tx=x+b[i][0];
			ty=y+b[i][1];//坐标增加 
			//坐标必须在指定范围内 
			if(tx>n-1||tx<0||ty>m-1||ty<0)  //if(x>n-1||x<0||y>m-1||y<0||a[x][y]=='S'||a[x][y]=='#')
				continue;  
				
			if(book[tx][ty]==0&&a[tx][ty]!='#')//该点可走 
			{
				book[tx][ty]=1;
				
				dfs(tx,ty,way+1);
				book[tx][ty]=0; 
			}
		} 
		return;//遍历完之后回退 
//	}
}
int main()
{
	int i,j,s1,s2; //s1,s2为起始点坐标 
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
	{ 
		scanf("%s",a[i]);
		for(j=0;j<m;j++)
			if(a[i][j]=='S')
			{
				s1=i;
				s2=j;
			}//起始点坐标 
	}
	
	/*********/
	book[s1][s2]=1;
	/*********/
	
	dfs(s1,s2,0);//初始状态 
	
	if(minway==9999999)
		printf("Sad");
	else 
		printf("%d",minway);
	return 0;
}

当然,这个的初始版本也是有一堆错误,麻烦师傅帮我改了很久QWQ(上面一些弄成注释的语句就是开始的错误点,也加强了一下深搜吧)

BFS初始版本

问了师傅之后,师傅给我讲了一下原理。大致就是最开始从起点出发,向周围四个方向走,这四个方向的点(符合条件)记为一级点,然后从每一个一级点出发,向四个方向扩展,找到符合条件的点就记为二级点…依次类推,直到在搜索过程中找到终点,则退出循环。代码实现如下(TLE)

#include<stdio.h>
struct point
{
	int x;
	int y;
	char real;
	int book;//标记是否走过 
}all[90][90]={0,0,'\0',0},//存放所有点的坐标及相关值
a[90][90]={0,0,'\0',0};//邻接表(下标为级数和序号)  
int min(int a,int b)
{
	return a>b?b:a;
}
int n,m;//地图面积   
int rank=0,record[90]={0};//邻接表级数;每一级元素的序号(record下标代表对应级数)
int minway=99999999;//最短路径 
int bfs(int q,int p)
{
	int i,j,b[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//增加相应坐标 
	rank++;
	for(i=0;i<4;i++)//四个方向查找 
	{
		q+=b[i][0];
		p+=b[i][1]; 
		if(q>=n||q<0||p>=m||p<0||all[q][p].real=='S'||all[q][p].real=='#')
		{
			q-=b[i][0];
			p-=b[i][1]; 
			continue;
		} 
		if(all[q][p].book==0) 
		{
			all[q][p].book=1;
			//代表此点已经存放到邻接表中  
			a[rank][record[rank]].x=q;
			a[rank][record[rank]].y=p;
			a[rank][record[rank]].real=all[q][p].real;
			record[rank]++;
		}
		q-=b[i][0];
		p-=b[i][1];
	}
	rank--;
}
int main()
{
	int i,j,stmp=0;
	char s[90];
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)//将点的全部信息填充进all中 
	{ 
		scanf("%s",s);
		for(j=0;j<m;j++)
		{
			all[i][j].x=i;
			all[i][j].y=j;
			all[i][j].real=s[j];
			all[i][j].book=0; 
		}
	}
	for(i=0;i<n;i++)
	{
		for(j=0;j<m;j++)
			if(all[i][j].real=='S')
			{
				stmp=-1;
				a[0][0].x=i; 
				a[0][0].y=j;
				a[0][0].real=all[i][j].real; 
				break;
			}
		if(stmp==-1)
			break; 
	}
	//填充邻接表
	record[0]=1;
	int flag=1;
	while(record[rank]!=-1&&flag==1)
	{
		int www=record[rank];
		for(i=0;i<www;i++)
			bfs(all[a[rank][i].x][a[rank][i].y].x,all[a[rank][i].x][a[rank][i].y].y);
		rank++;
		for(i=0;i<record[rank];i++)
			if(a[rank][i].real=='E')
			{
				flag=-1;
				break;
			}
		if(record[rank]==0)
		{
			flag=-2;
			break;
		}
	}
	if(flag==-1)
		printf("%d",rank);
	if(flag==-2)
		printf("Sad");
	return 0;
}

虽然结果确实对了,但是!!!由于for套for的循环太多了,耗时实在是太长了,而为什么循环会这么多呢,这就在于我真的去模拟了一个邻接表,然后遍历邻接表(C语言萌新只会用C的东西写,勿喷),就挺蠢的。

BFS最终版本

初学者没有学过C++,就不知道怎么用队列的那个函数,于是在师傅的指导下参考了啊哈C对此类题的处理方法,模拟了一个队列(结构体数组),再加上了book数组和一个尾指针(head),成功地AC了(欢呼)

#include<stdio.h>
struct queue
{
	int x;
	int y;
	char real;	
	int step;
}point[7500]={0,0,'\0',0,0};
int main()
{
	int i,j;
	int book[90][90];
	int head=1,tail=0,n,m,stmp=1,flag=1,now=0;
	int b[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
	char s[90][90];
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
	{ 
		scanf("%s",s[i]);
		if(stmp==1)
			for(j=0;j<m;j++)
				if(s[i][j]=='S')
				{
					point[0].x=i,point[0].y=j,book[i][j]=1,point[0].real='S';
					stmp=-1;
				}
	}
	while(tail<head&&flag==1)
	{
		for(i=0;i<4;i++)
		{
			now++;
 			point[now].x=point[tail].x+b[i][0];
			point[now].y=point[tail].y+b[i][1];
			if(point[now].x>=n||point[now].x<0||point[now].y>=m||point[now].y<0||s[point[now].x][point[now].y]=='S'||s[point[now].x][point[now].y]=='#'||book[point[now].x][point[now].y]==1)
			{
				point[now].x=0;
				point[now].y=0; 
				now--;
				continue;
			}
			if(book[point[now].x][point[now].y]==0) 
			{
				book[point[now].x][point[now].y]=1;
				point[now].step=point[tail].step+1;//下一级是上一级的步数加一 
				head++;//队列变长 
			}
			if(s[point[now].x][point[now].y]=='E')
			{
				flag=-1;
				break;
			}
		}
		if(flag==-1)
			break;
		if(head==tail)
		{
			flag=-2;
			break;
		} 
		tail++;//每一点扩展完毕之后才能抛弃 
	}
	if(flag==-1)
		printf("%d",point[now].step);
	else
		printf("Sad");
	return 0;
}

数据扩大之后的改进版本

这次数据直接就从85变成了8500,面积突然就扩大了很多。以下代码用了一个C++的bool函数,节省了空间,再加上使用了全局变量,可开辟空间也增大了,耗时和空间都得到了很大的优化。

#include<stdio.h>
#include<iostream> 
using namespace std;
struct queue
{
	int x;
	int y;
	char real;	
	int step;
}point[1000005]={0,0,'\0',0,0};//不要开得过大 
char s[8505][8505];
bool book[8505][8505];
int main()
{
	int i,j;
	int head=1,tail=0,n,m,stmp=1,flag=1,now=0;
	int b[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
	{ 
		scanf("%s",s[i]);
		if(stmp==1)
			for(j=0;j<m;j++)
				if(s[i][j]=='S')
				{
					point[0].x=i,point[0].y=j,book[i][j]=1,point[0].real='S';
					stmp=-1;
				}
	}
	while(tail<head&&flag==1)
	{
		for(i=0;i<4;i++)
		{
			now++;
 			point[now].x=point[tail].x+b[i][0];
			point[now].y=point[tail].y+b[i][1];
			if(point[now].x>=n||point[now].x<0||point[now].y>=m||point[now].y<0||s[point[now].x][point[now].y]=='S'||s[point[now].x][point[now].y]=='#'||book[point[now].x][point[now].y]==1)
			{
				point[now].x=0;
				point[now].y=0; 
				now--;
				continue;
			}
			if(book[point[now].x][point[now].y]==0) 
			{
				book[point[now].x][point[now].y]=1;
				point[now].step=point[tail].step+1;//下一级是上一级的步数加一 
				head++;//队列变长 
			}
			if(s[point[now].x][point[now].y]=='E')
			{
				flag=-1;
				break;
			}
		}
		if(flag==-1)
			break;
		if(head==tail)
		{
			flag=-2;
			break;
		} 
		tail++;//每一点扩展完毕之后才能抛弃 
	}
	if(flag==-1)
		printf("%d",point[now].step);
	else
		printf("Sad");
	return 0;
}

以上
算法真的挺难的orz

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值