BFS基本思想
宽度优先搜索(Breadth First Search,BFS),简称宽搜,又称广度优先搜索。它是从初始结点开始,应用产生式规则和控制策略生成第一层结点,同时检查目标结点是否在这些生成的结点中。若没有,再用产生式规则将所有第一层结点逐一拓展,得到第二层结点,并逐一检查第二层结点是否包含目标结点。若没有,再用产生式规则拓展第二层结点。如此依次拓展,检查下去,直至发现目标结点为止。如果拓展完所有结点,都没有发现目标结点,则问题无解。
BFS适合求最少步骤或者最短解序列这类最优解问题
例如,求最大连通块问题。
BFS基本程序框架
void BFS{
while (front <= rear){
// 当队列非空时做,front 和 rear 分别表示队列的头指针和尾指针
if (找到目标状态)
做相应处理(如退出循环输出解、输出当前解、比较解的优劣);
else{
拓展头结点 ;
if( 拓展出的新结点没出现过 ){
rear++;
将新结点插到队尾 ;
}
}
front++;// 取下一个结点
}
}
BFS结构体+STL框架
struct node //结构体用于保存每一状态信息
{
......
};
void bfs()
{
queue<node> Q; // 定义存放结构体的队列Q
起点入队
标记起点
while(!Q.empty()) // 队非空
{
node u=Q.front(); //获取队首信息(结构体)
for(拓展接下来所有可能的状态)
{
得到并记录新的状态信息
判断状态是否合法
若合法
{
当前标记为已访问
Q.push((node){...});//状态入队
判断是否到达目标
若满足,输出答案,return ;
}
}
Q.pop();//每次从队首把所有可能的状态走完,队首要出队
}
执行到这一行,说明无法实现,根据题意输出
}
瓷砖
视频讲解
宽搜-瓷砖
【问题分析】读入字符数组,找到小林的初始位置“@”,并把坐标入队,作为队头元素。BFS检查队头元素的上下左右四个位置是否是黑色瓷砖,是则入队,…不断取出队头元素进行四个方向的拓展,直到队列为空。
为了避免一个位置被多次重复走到,定义一个布尔型数组a[i][j]用来判重。
本题是搜索的一个重要应用,所谓的–求连通块问题
#include<bits/stdc++.h>
using namespace std;
bool a[52][52];
int dx[5]={0,1,-1,0,0};//方向数组
int dy[5]={0,0,0,1,-1};
int q[3][2510];//队列,q[1]维护x坐标;q[2]维护y坐标
int h,w,front,rear,x,y;
int main()
{
cin>>h>>w;
char c;
for(int i = 1;i<=w;i++)
for(int j = 1;j<=h;j++)
{
cin>>c;
if(c=='.') a[i][j]=true;
else if(c=='@')
{
x=i,y=j;
a[i][j]=false;
}
}
q[1][1]=x;q[2][1]=y;
front = 1;rear = 1;//注意f>r队列才为空
while(front<=rear)
{
for(int i = 1;i<=4;i++)
{
x=q[1][front]+dx[i];
y=q[2][front]+dy[i];
if(a[x][y])//入队
{
rear++;
q[1][rear]=x;
q[2][rear]=y;
a[x][y]=false;
}
}
front++;//出队
}
cout<<rear;
}
图的广度优先搜索
如图表示的是从城市A到城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。
【问题分析】
看到这图很容易想到用邻接距阵来表示,0表示能走,1表示不能走。如图
首先想到的是用队列的思想。a数组是存储扩展结点的队列,a[i]记录经过的城市,b[i]记录前趋城市,这样就可以倒推出最短线路。具体过程如下:
(1) 将城市A入队,队首为0、队尾为1。
(2)将队首所指的城市所有可直通的城市入队(如果这个城市在队列中出现过就不入队,可用一布尔数组s[i]来判断),将入队城市的前趋城市保存在b[i]中。然后将队首加1,得到新的队首城市。重复以上步骤,直到搜到城市H时,搜索结束。利用b[i]可倒推出最少城市线路。
图的宽度优先搜索
#include<iostream>
#include<cstring>
using namespace std;
int ju[9][9]={{0,0,0,0,0,0,0,0,0},
{0,1,0,0,0,1,0,1,1},
{0,0,1,1,1,1,0,1,1},
{0,0,1,1,0,0,1,1,1},
{0,0,1,0,1,1,1,0,1},
{0,1,1,0,1,1,1,0,0},
{0,0,0,1,1,1,1,1,0},
{0,1,1,1,0,0,1,1,0},
{0,1,1,1,1,0,0,0,1}};
int a[101],b[101];
bool s[9]; //初始化
int out(int d) //输出过程
{
cout<<char(a[d]+64);
while (b[d])
{
d=b[d];
cout<<"--"<<char(a[d]+64);
}
cout<<endl;
}
void doit()
{
int head,tail,i;
head=0;tail=1; //队首为0、队尾为1,所以h==t则队列为空
a[1]=1; //记录经过的城市
b[1]=0; //记录前趋城市
s[1]=1; //表示该城市已经到过
do //步骤2
{
head++; //队首加一,出队
for (i=1;i<=8;i++) //搜索可直通的城市
if ((ju[a[head]][i]==0)&&(s[i]==0)) //判断城市是否走过
{
tail++; //队尾加一,入队
a[tail]=i;
b[tail]=head;
s[i]=1;
if (i==8)
{
out(tail);
head=tail;
break; //第一次搜到H城市时路线最短
}
}
}while (head<tail);//注意此处h==t则退出循环
}
int main() //主程序
{
memset(s,false,sizeof(s));
doit(); //进行Bfs操作
return 0;
}
关系网络
关系网络
【问题解析】
本题是宽搜的一个重要应用“求最优值问题”。
先设答案ans = 0.把x加入队列并设置队头元素,从队头开始进行宽搜,穷举邻接矩阵的第x行,看x认识谁(判断a[x,j]=1),认识的人(j)全部依次入队,并且ans++,如果出现了y,则输出ans,结束搜索,否则,取出队头元素继续宽搜。
#include<bits/stdc++.h>
using namespace std;
int a[101][101];
int b[101],f[101],k[101];
int n,x,y,front,rear,tx;
int main()
{
cin>>n>>x>>y;
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
cin>>a[i][j];
}
front = 1;rear = 1;
f[1]=x;k[1]=0;b[x]=1;
while(front<=rear)
{
tx=f[front];
if(tx==y)
{
cout<<k[front]-1<<endl;
break;
}
for(int i = 1;i<=n;i++)
{
if(a[i][tx]==1 && (b[i]==0))
{
rear ++;
f[rear]=i;
b[i]=1;
k[rear]=k[front]+1;
}
}
front++;
}
return 0;
}
飞跃原野
飞跃原野
【问题分析】
这是一道三维DFS题目,仔细体会第三维的添加思想,很重要!
先不考虑“飞行”,那么只有一种运动方式,就是最基本的宽搜。可以设状态F[i,j]表示到达位置(i,j)的最短时间。
如果处理“飞行”运动呢?可以设F[i,j,r]表示到达位置(i,j),还剩r的飞行时间的最短时间。状态的拓展就是从当前位置可以步行1步或者飞行r,r从2穷举到D,因为飞行距离为1是没有意义的,则搜索的记录的队列如下:
#include <cstdio>//BFS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
char ma[110][110];
bool vis[110][110][110];
typedef struct node
{
int x,y,d,time;
};
int n,m,d;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
void bfs()
{
queue <node> Q;
node t;int i,j;
t.x=0;t.y=0;t.time=0;t.d=d;
Q.push(t);
while(!Q.empty())
{
node v=Q.front(); Q.pop();
if(v.x==m-1&&v.y==n-1)
{
cout<<v.time<<endl;
return ;
}
for(i=0;i<4;i++)
{
t.x=v.x+dir[i][0];
t.y=v.y+dir[i][1];
t.d=v.d;
if(t.x>=0&&t.x<m&&t.y>=0&&t.y<n&&!vis[t.x][t.y][t.d]&&ma[t.x][t.y]=='P')//run
{
vis[t.x][t.y][t.d]=1;
t.time=v.time+1;
Q.push(t);
}
for(j=2;j<=v.d;j++)//fly
{
t.x=v.x+j*dir[i][0];
t.y=v.y+j*dir[i][1];
if(t.x>=0&&t.x<m&&t.y>=0&&t.y<n&&!vis[t.x][t.y][v.d-j]&&ma[t.x][t.y]=='P')
{
vis[t.x][t.y][v.d-j]=1;
t.d=v.d-j;
t.time=v.time+1;
Q.push(t);
}
}
}
}
puts("impossible");
}
int main()
{
int i,j;
while(cin>>m>>n>>d)
{
memset(vis,0,sizeof(vis));
for(i=0;i<m;i++)
cin>>ma[i];
bfs();
}
return 0;
}