【算法】广度优先搜索(BFS)II

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lifehack/article/details/17229283

1. 迷宫问题


迷宫的最直接的表示无疑是二维数组,用0表示可通过,1表示墙壁。如下所示是一个简单的迷宫

0  1  0  0  0 
0  1  0  1  0 
0  0  0  0  0 
0  1  1  1  0 
0  0  0  1  0 

假定从左上角(0, 0)进入迷宫,从右下角(4, 4)走出迷宫;移动方向共有4个:左右上下。求解最少步数的移动策略。


按步骤,我们可以画出搜索树[1]

《【算法】广度优先搜索(BFS)II》

节点(4, 4)处在第9层,显然最少步数应是8,对应地搜索策略亦可求出:(0, 0)->(1, 0)->(2, 0)->(2, 1)->(2, 2)->(2, 3)->(2, 4)->(3, 4)->(4, 4)


可以发现,迷宫问题与前一篇中的knight moves问题相类似,可BFS求解。


2. Referrence


[1] raphealguo, 【算法入门】广度/宽度优先搜索(BFS).


3. 问题


3.1 POJ 3984


要求打印最少步数的移动策略,用parent记录父节点以便于打印。


源代码:

3984Accepted176K0MSC++1282B2013-12-09 20:44:41
#include<iostream>
#include <queue>
using namespace std;

int maze[5][5],visit[5][5];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};

struct point
{
	int x,y;
}parent[5][5];
point start,end;

void bfs()
{
	int que_size,i;
	point head,next;
	queue<point>que;
	
	start.x=0; start.y=0;                                      //initialization
	end.x=4; end.y=4;
	memset(visit,0,sizeof(visit));
	visit[start.x][start.y]=1;
	que.push(start);
	parent[start.x][start.y].x=-1; parent[start.x][start.y].y=-1;

	while(!que.empty())
	{
		que_size=que.size();
		while(que_size--)
		{
			head=que.front();
			que.pop();
			if(head.x==end.x&&head.y==end.y)
				return;
			
			for(i=0;i<4;i++)
			{
				next.x=head.x+dir[i][0]; next.y=head.y+dir[i][1];
				if(next.x<0||next.x>4||next.y<0||next.y>4||maze[next.x][next.y]) continue;
				if(!visit[next.x][next.y])
				{
					visit[next.x][next.y]=1;
					que.push(next);
					parent[next.x][next.y].x=head.x;
					parent[next.x][next.y].y=head.y;
				}
			}
		}
	}
}

void output(point k)                //output the result
{
	if(k.x!=-1||k.y!=-1)
	{
		output(parent[k.x][k.y]);
		printf("(%d, %d)\n",k.x,k.y);
	}
}

int main()
{
	int i,j;
	for(i=0;i<5;i++)
		for(j=0;j<5;j++)
			scanf("%d",&maze[i][j]);
	bfs();
	output(end);
	return 0;
}


3.2 POJ 2157


相较于简单的迷宫问题,本题增加了限制条件:有门、钥匙,并且若要打开门,需要集齐所有该门的钥匙。


在BFS结束时,判断曾访问过的门是否能被打开;可以用一个队列来维护曾访问过的门。


源代码:

2157Accepted212K16MSC++2280B2013-12-10 09:35:45
#include<iostream>
#include <queue>
using namespace std;

#define MAX 20

int N,M,can,visit[MAX][MAX];
char maze[MAX][MAX];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};

struct point
{
	int x,y;
};
point start,end;

struct
{
	int all,find;         //门共有钥匙数、已找到的钥匙数
}door[5];

void init()               //initialization
{
	int i,j;
	memset(door,0,sizeof(door));
	for(i=0;i<M;i++)
		for(j=0;j<N;j++)
		{
			cin>>maze[i][j];
			if(maze[i][j]=='S')
			{
				start.x=i; start.y=j;
			}
			if(maze[i][j]=='G')
			{
				end.x=i; end.y=j;
			}
			if(maze[i][j]>='a'&&maze[i][j]<='e')
				door[maze[i][j]-'a'].all++;
		}
}

void bfs()
{
	int que_size,visit_door_size,i,temp;
	point head,next;
	queue<point>que;
	queue<point>visit_door;
	
	can=0;
	memset(visit,0,sizeof(visit));
	visit[start.x][start.y]=1;
	que.push(start);

	while(!que.empty())
	{
		que_size=que.size();
		while(que_size--)
		{
			head=que.front();
			que.pop();
			if(head.x==end.x&&head.y==end.y)
			{
				can=1;
				return;
			}
			
			for(i=0;i<4;i++)
			{
				next.x=head.x+dir[i][0]; next.y=head.y+dir[i][1];
				if(next.x<0||next.x>=M||next.y<0||next.y>=N||maze[next.x][next.y]=='X') continue;
				
				if(!visit[next.x][next.y])
				{
					if(maze[next.x][next.y]>='a'&&maze[next.x][next.y]<='e')         //找到钥匙
						door[maze[next.x][next.y]-'a'].find++;
					else if(maze[next.x][next.y]>='A'&&maze[next.x][next.y]<='E')
					{
						temp=maze[next.x][next.y]-'A';
						if(door[temp].all>door[temp].find)                           //没找全钥匙
						{
							visit_door.push(next);
							continue;
						}
					}
					
					visit[next.x][next.y]=1;
					que.push(next);
				}
			}
		}
        if(que.empty())                                       //判断能否打开曾访问过的门
		{
			visit_door_size=visit_door.size();
			while(visit_door_size--)
			{
				head=visit_door.front();
				visit_door.pop();
				temp=maze[head.x][head.y]-'A';
				if(door[temp].all==door[temp].find)
				{
					visit[head.x][head.y]=1;
					que.push(head);
				}
				else visit_door.push(head);
			}
		}
	}
}

int main()
{
	while(~scanf("%d%d",&M,&N)&&M)
	{
		init();
		bfs();
		if(can) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}


3.3 POJ 2251


这个迷宫是三维的,无非是移动方向变成了6个。


源代码:

2251Accepted388K32MSC++1629B2013-12-10 10:18:47
#include<iostream>
#include <queue>
using namespace std;

#define MAX 30

int L,R,C,possible,steps,visit[MAX][MAX][MAX];
char dungeon[MAX][MAX][MAX];
int dir[6][3]={{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};

struct point
{
	int x,y,z;
};
point start,end;

void init()               //initialization
{
	int i,j,k;
	steps=0;
	for(i=0;i<L;i++)
		for(j=0;j<R;j++)
			for(k=0;k<C;k++)
			{
				cin>>dungeon[i][j][k];
				if(dungeon[i][j][k]=='S')
				{
					start.x=i; start.y=j; start.z=k;
				}
				else if(dungeon[i][j][k]=='E')
				{
					end.x=i; end.y=j; end.z=k;
				}
			}
}

void bfs()
{
	int que_size,i;
	point head,next;
	queue<point>que;
	
	possible=0;
	memset(visit,0,sizeof(visit));
	visit[start.x][start.y][start.z]=1;
	que.push(start);
	
	while(!que.empty())
	{
		que_size=que.size();
		while(que_size--)
		{
			head=que.front();
			que.pop();
			if(head.x==end.x&&head.y==end.y&&head.z==end.z)
			{
				possible=1;
				return;
			}
			
			for(i=0;i<6;i++)
			{
				next.x=head.x+dir[i][0]; next.y=head.y+dir[i][1]; next.z=head.z+dir[i][2];
				if(next.x<0||next.x>=L||next.y<0||next.y>=R||next.z<0||next.z>=C||dungeon[next.x][next.y][next.z]=='#') continue;
				
				if(!visit[next.x][next.y][next.z])
				{				
					visit[next.x][next.y][next.z]=1;
					que.push(next);
				}
			}
		}
		steps++;       
	}
}

int main()
{
	while(~scanf("%d%d%d",&L,&R,&C)&&L)
	{
		init();
		bfs();
		if(possible) printf("Escaped in %d minute(s).\n",steps);
		else printf("Trapped!\n");
	}
	return 0;
}



点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注