广度优先搜索(Breadth First Search , BFS)是一个分层搜索的过程,没有回退过程,是非递归的。
为避免重复访问,需要一个状态数组visited[n]来存储各顶点的访问状态。为实现逐层访问,bfs算法在实现时需要一个队列,以记忆正在访问的这一层和上一层的顶点,以便于向下一层访问。
例题(1):营救(rescue)
输入描述:"." :道路 "r" :Angle的朋友 "#" :墙壁 "x" :警卫
分析:本题要求从r位置出发到达Angel所在的位置且所需时间最少,适合bfs求解。但在本题中,步数最少的解不一定是最优解。
为求出最优解,采用如下的思路进行bfs搜索。
(1)将Angel的朋友到达某个方格的状态用一个结构体point表示,该结构体包含了Angel的朋友到达该方格时所走过的步数及所花费的时间;在bfs搜索中,队列中的结点是point型数据.
(2)定义一个二维数组mintime,mintime[i][j]表示Angel的朋友走到(i,j)位置所需的最少时间;在bfs搜索过程中,从当前位置走到相邻的位置(x,y)时,只有当该种走法比走到(x,y)所需的时间更少时,才会把当前走到(x,y)位置的状态入队列,否则是不会入队列的。
(3)不能一判断出到达目标位置就退出bfs过程,否则求出的最小时间仅仅是从r到达a最小步数的若干方案中的最小时间,不一定是最优解,一定要等到队列为空,才能得出结论。
另外在题目中未使用visited[n]数组,因为只有当下一个位置比上一个所需的时间少才能入队列,所以到达(x,y)的最少时间肯定是有下界的。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define maxint 0x3f3f3f3f
#define MAXN 200
using namespace std;
struct point
{
int x,y;
int step;
int time;
};
queue<point>Q;
int n,m;
char map[MAXN][MAXN];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int mintime[MAXN][MAXN];
int ax,ay;///Angel所在的位置
int bfs(point s);
int main()
{
int i,j;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(mintime,maxint,sizeof(mintime));
memset(map,0,sizeof(map));
for(i=0;i<n;i++)
scanf("%s",map[i]);
int sx,sy;
point start;
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
if(map[i][j]=='a'){ax=i,ay=j;}
else if(map[i][j]=='r'){sx=i,sy=j;}
}
start.x=sx;
start.y=sy;
start.step=0;
start.time=0;
mintime[sx][sy]=0;
int mint=bfs(start);
if(mint<maxint)
printf("%d\n",mint);
else
printf("can not arrived\n");
}
return 0;
}
int bfs(point s)
{
int i,j;
Q.push(s);
point hd;///队列头的位置
while(!Q.empty())
{
hd=Q.front();
Q.pop();
for(i=0;i<4;i++)
{
int x=hd.x+dir[i][0];
int y=hd.y+dir[i][1];
if(x>=0&&y>=0&&x<=n-1&&y<=m-1&&map[x][y]!='#')
{
point t;
t.x=x;
t.y=y;
t.step=hd.step+1;
t.time=hd.time+1;
if(map[x][y]=='x') t.time++;///杀死警卫的时间
///如果比最少时间少,则将t入队列
if(t.time<mintime[x][y])
{
mintime[x][y]=t.time;
Q.push(t);
}
}
}
}
return mintime[ax][ay];
}
广度优先搜索的伪代码
(1)若用邻接表存储图
dfs(顶点i)
{
visited[i]=1;
将顶点i入队列;
while(队列不为空)
{
取出队列头的顶点,设为k;
p=顶点k的边链表表头指针;
while(p不为空)
{
///设指针p指向的边结点所表示的边的另一个顶点为j;
if(顶点j未访问过)
{
将顶点j标记为访问;
将顶点j入队列;
}
p=p->next;
}
}
}
(2)用邻接矩阵存储图
dfs(顶点i)
{
visited[i]=1;
将顶点i入队列;
while(队列不为空)
{
取出队列头的顶点,设为k;
for(j=0;j<n;j++)
{
///j是k的邻接点,且未被访问过
if(map[k][j]==1&&!visited[j])
{
将顶点j标记为访问;
将顶点j入队列;
}
}
}
}