BFS基本模板
同DFS的问题引入,计算迷宫中的最短路径问题:方法是通过一层一层扩展的方法来达到目标位置,扩展时每发现一个点就将这个点加入到队列中去,直到达到目标位置。这个方法的核心思路是模拟路线,每当没写一个新的可能,就把这个可能入列,直到达到位置,然后在所以的队列中,取最短的队列即可解决问题。
基本模板
队列操作准备:
struct note
{
int x;//表示横坐标
int y;//表示纵坐标
int s;//表示步数
};
struct note queue[2501];//由于地图是50*50的,所以队列总长度不会超过2500
int head,tail;
int a[51][51];//存储地图
int book[51][51]={0};//用来记录哪个点已经被走过了的数组
//最开始的时候需要对队列进行初始化,即将队列设置为空
head=1;
tail=1;
queue[tail].x=1;
queue[tail].y=1;
queue[tail].s=0;
tail++;
book[1][1]=1;
//然后从(1,1)开始,先尝试往右走到达了(1,2)
//具体的操作是y+1
tx=queue[head].x;
ty=queue[head].y;
//接着判定是否越界?
if(tx<1||tx>n||ty<1||ty>m)
{
continue;
}
//再接着判断是否下一个点是障碍物或者已经走过?
if(book[tx][ty]==0&&a[tx][ty]==0)//如果没有走过,而且不是障碍物,那么这个点就是我们想要的点
{
book[tx][ty]=1;//BFS的算法下,每一个点通常只入队一次。
queue[tail].x=tx;
queue[tail].y=ty;
queue[tail].s=queue[head].s+1;//步数更新
tail++;
}
刚才那一步是入列操作核心,我们发现从(1,1)可以到达(1,2)也可以到达(2,1),那么我们也需要将(2,1)也加入队列操作与刚才一致。在将这两点完全加入队列之后,(1,1)就没有用了,此时我们对(1,1)出列处理,出列的操作非常简单,只需:
head++;
表示前头的指向向后指了一位。而这样指了这么一位之后,就要重复刚才的过程了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
struct note
{
int x;//表示横坐标
int y;//表示纵坐标
int f;//父亲在队列中的编号,本题不要求输出路劲,可以不需要f
int s;//表示步数
};
int main()
{
struct note queue[2501];
int a[51][51]={0},book[51][51]={0};//记录地图,记录路径
//定义一个表示方向的数组
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int head,tail;
int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
scanf("%d%d%d%d",&startx,&starty,&p,&q);
head=1;
tail=1;
queue[tail].x=startx;
queue[tail].y=starty;
queue[tail].s=0;
queue[tail].f=0;
tail++;
book[startx][starty]=1;
flag=0;//用来表示状态量,如果没有达到我们的目的地,那么就令其一直为0
//当队列不是空的时候我们循环
while(head<tail)
{
for(k=0;k<4;k++)
{
tx=queue[head].x+next[k][0];
ty=queue[head].y+next[k][1];
if(tx<1||tx>n||ty<1||ty>m)
{
continue;
}
if(a[tx][ty]==0&&book[tx][ty]==0)
{
//标记点已经走过
book[tx][ty]=1;
//queue的head是用来存储当前状态的,queue的tail是用来存储将要存储的东西的
queue[tail].x=tx;
queue[tail].y=ty;
queue[tail].f=head;//f是用来记录当前路径的,如果题目有要求说需要求出这条路是怎么走的
//就可以引入这个变量来表示
queue[tail].s=queue[head].s+1;
tail++;//准备下一次插入
}
//接着判断是否到达目标点,如果到了就停止扩展啦
if(tx==p&&ty==q)
{
flag=1;
break;
}
}
if(flag)
{
break;
}
head++;
}
printf("%d",queue[tail-1].s);
return 0;
}
宝岛探险问题
目前给出小人在由多个岛屿中组成的地图的初始位置,假定降落点的岛屿以海洋为边界,所有的都属于这个岛屿,求出岛屿的面积。
由于每个点只入队一次,那么这道题用BFS可以非常简单地写出来,代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#define num 105*105
int a[105][105];//地图存储
int book[105][105];//地图标记
int next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//移动数组,顺时针
struct note
{
int x;
int y;
};
int main()
{
int n,m,startx,starty,tx,ty,head,tail,sum,k;
struct note que[num];
head=1,tail=1;
scanf("%d%d%d%d",&n,&m,&startx,&starty);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
head=1,tail=1,sum=1;
que[tail].x=startx,que[tail].y=starty;
book[startx][starty]=1;
tail++;
while(head<tail)
{
for(k=0;k<4;k++)
{
tx=que[head].x+next[k][0];
ty=que[head].y+next[k][1];
if(tx<1||tx>n||ty<1||ty>m)
{
continue;
}
else if(a[tx][ty]>0&&book[tx][ty]==0)
{
book[tx][ty]=1;
que[tail].x=tx,que[tail].y=ty;
sum++,tail++;
}
}
head++;
}
printf("%d",sum);
return 0;
}
SCAU 18276 走迷宫
题目描述
18276 走迷宫
时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0
题型: 编程题 语言: G++;GCC;VC
Description
有一个N*M的格子迷宫,1代表该格子为墙,不能通过,0代表可以通过,另外,在迷宫中
有一些传送门,走到传送门的入口即会自动被传送到传送门的出口(一次传送算1步)。人在迷宫中可以尝试
上下左右四个方向移动。现在给定一个迷宫和所有传送门的出入口,以及起点和终点,
问最少多少步可以走出迷宫。如果不能走出迷宫输出“die”。
输入格式
该程序为多CASE,第1行为CASE的数量
每一个CASE,第1行为两个数N(行)和M(列)
然后N行每行M个数
之后是一个数W,为传送门的数量
之后每行一个传送门的入口坐标c1(行),r1(列)和出口坐标c2,r2
之后是起点坐标和终点坐标sc(行) sr(列) ec(行) er(列)
注:传送门出入口和起点坐标和终点坐标不会出现在墙的位置
所有数字不超过100
输出格式
如题
输入样例
2
4 3
011
011
110
110
1
1 0 2 2
0 0 3 2
2 2
01
10
0
0 0 1 1
输出样例
3
die
问题分析
观察数据范围(100*100),如果用的是dfs的解法,将会超时,那么有两个方案:
一个设定步数数组,提前预判剪枝,可以大幅减少时间复杂度.
采用BFS,最优解法
解法思路
首先这道题由于是有传送点的情况,那么也就是说,在后来队列尾部加入节点的时候,是多了选择的,本来只是上下左右走的,但是有了传送门这个选择,可以导致我们扩展出来的节点不同。那么首先需要做的是,判断该点是不是传送门
使用的是如下函数
int IsTp(int x,int y,int n)/*判断是不是tp点*/
{
int i;
for(i=0; i<n; i++)
{
if(goin[i][0]==x && goin[i][1]==y)
{
return i;
}
}
return -1;
}
然后在我们正常的BFS过程中,这道题有什么不一样呢?
首先,如果队头节点是传送门节点的话,那么扩展出来的节点只会有一个
其次,队头节点是传送门节点的话,扩展出来的节点需要我们手动从数组中调出来
最后,需要特别注意的是,小人的起始点可以是传送门,那么也就意味着,起始点其实是变的,就是说题目给的起始点是一个烟雾弹,起始点其实是当前传送门传送过去的那个点。
AC代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int M=100+5;
const int N=1e5+5;
char a[M][M];
int book[M][M],goin[M][2],out[M][2];/*记录迷宫的出口和入口*/
struct node
{
int x;
int y;
int step;
};
int IsTp(int x,int y,int n)/*判断是不是tp点*/
{
int i;
for(i=0; i<n; i++)
{
if(goin[i][0]==x && goin[i][1]==y)
{
return i;
}
}
return -1;
}
int main()
{
/*BFS*/
int i,j,startx,starty,targetx,targety,w,n,m;
bool IsFind=false;
int dir[4][2]= {{0,1},{1,0},{0,-1},{-1,0}}; /*方向数组*/
struct node q[N];/*最多会扩展出M个节点*/
ios::sync_with_stdio(false);
int kase;
cin>>kase;
while(kase--)
{
memset(book,0,sizeof(book));
memset(a,0,sizeof(a));
memset(out,0,sizeof(out));
memset(goin,0,sizeof(goin));
memset(q,0,sizeof(q));
IsFind=false;
cin>>n>>m;
int head=0,tail=0,tx,ty;
for(i=0; i<n; i++)
{
cin>>a[i];/*读入数据*/
}
cin>>w;
for(i=0; i<w; i++)
{
for(j=0; j<2; j++)
{
cin>>goin[i][j];
}
for(j=0; j<2; j++)
{
cin>>out[i][j];
}
}
cin>>startx>>starty>>targetx>>targety;
/*队列初始化*/
q[tail].x=startx;
q[tail].y=starty;
book[startx][starty]=1;
q[tail].step=0;
tail++;
/*首先要检测起始点是不是传送门*/
if(IsTp(startx,starty,w)!=-1)/*是传送门*/
{
int pos=IsTp(startx,starty,w);
tx=out[pos][0],ty=out[pos][1];
q[tail].x=tx,q[tail].y=ty;
q[tail].step=q[head].step+1;
/*如果传送了,那么上一个点就没用了*/
head++;
tail++;
}
/*BFS循环*/
while(head<tail)
{
/*应对特殊情况 */
if(q[head].x==targetx && q[head].y==targety)
{
IsFind=true;
tail++;
q[tail-1].step=q[head].step;
break;
}
/*如果头节点只能扩展出到传送门的节点*/
if(IsTp(q[head].x,q[head].y,w)!=-1)
{
int pos=IsTp(q[head].x,q[head].y,w);
q[tail].x=out[pos][0];
q[tail].y=out[pos][1];
q[tail].step=q[head].step+1;
book[q[tail].x][q[tail].y]=1;
if(q[tail].x==targetx && q[tail].y==targety)
{
IsFind=true;
tail++;
break;
}
tail++;
head++;
continue;
}
for(int k=0; k<4; k++)
{
tx=q[head].x+dir[k][0];
ty=q[head].y+dir[k][1];
if(tx<0||tx>n||ty<0||ty>m)
{
continue;
}
if(book[tx][ty]==0 && a[tx][ty]=='0')/*如果可以走动*/
{
q[tail].x=tx,q[tail].y=ty;
q[tail].step=q[head].step+1;
book[tx][ty]=1;
if(tx==targetx&& ty==targety)
{
IsFind=true;
}
tail++;
}
}
if(IsFind)
{
break;
}
head++;
}
if(IsFind)
{
cout<<q[tail-1].step<<'\n';
}
else
{
cout<<"die"<<'\n';
}
}
return 0;
}