一些用BFS写题的过程感想(dalao误入)
题目:迷路小青蛙
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