宽度优先搜索--BFS

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;
}

更多练习

1、马的遍历
2、奇怪的电梯
3、流星雨

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信奥教练Andy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值