BFS定义
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
已知图G=(V,E)和一个源顶点s,宽度优先搜索以一种系统的方式探寻G的边,从而“发现”s所能到达的所有顶点,并计算s到所有这些顶点的距离(最少边数),该算法同时能生成一棵根为s且包括所有可达顶点的宽度优先树。对从s可达的任意顶点v,宽度优先树中从s到v的路径对应于图G中从s到v的最短路径,即包含最小边数的路径。该算法对有向图和无向图同样适用。
之所以称之为宽度优先算法,是因为算法自始至终一直通过已找到和未找到顶点之间的边界向外扩展,就是说,算法首先搜索和s距离为k的所有顶点,然后再去搜索和S距离为k+l的其他顶点。
————copy自百度百科
本质上,BFS就是图的一种遍历方式。
BFS和DFS对比
DFS:
深度优先搜索用栈(stack)来实现:
1、把根节点压入栈中。
2、每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
BFS:
广度优先搜索使用队列(queue)来实现:
1、把根节点放到队列的末尾。
2、每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
BFS模板
void bfs()
{
memset(mark,0);//0表示未走
queue q;
q.push(i);//起点入队
mark(i)=0;
while(!q.empty())
{
top=q.front;
for(...)
{
q.push(k);
}
q.pop();//扔掉队列起点
}
}
- 将起点入列
- 对队首操作
- 将队首周围的所有连接点入列
- 弹出队首
- 如果队列为空,则跳出循环
典型例题
下面用一道典型例题来讲解:(注:来自acwing,属于中档题)
题目
栋栋最近开了一家餐饮连锁店,提供外卖服务。
随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。
栋栋的连锁店所在的区域可以看成是一个 n×n 的方格图(如下图所示),方格的格点上的位置上可能包含栋栋的分店(绿色标注)或者客户(蓝色标注),有一些格点是不能经过的(红色标注)。
方格图中的线表示可以行走的道路,相邻两个格点的距离为 1。
栋栋要送餐必须走可以行走的道路,而且不能经过红色标注的点。
送餐的主要成本体现在路上所花的时间,每一份餐每走一个单位的距离需要花费 1 块钱。
每个客户的需求都可以由栋栋的任意分店配送,每个分店没有配送总量的限制。
现在你得到了栋栋的客户的需求,请问在最优的送餐方式下,送这些餐需要花费多大的成本。
输入格式
输入的第一行包含四个整数 n,m,k,d分别表示方格图的大小、栋栋的分店数量、客户的数量,以及不能经过的点的数量。
接下来 m 行,每行两个整数 xi,yi表示栋栋的一个分店在方格图中的横坐标和纵坐标。
接下来 k行,每行三个整数 xi,yi,ci分别表示每个客户在方格图中的横坐标、纵坐标和订餐的量。(注意,可能有多个客户在方格图中的同一个位置)
接下来 d行,每行两个整数,分别表示每个不能经过的点的横坐标和纵坐标。
输出格式
输出一个整数,表示最优送餐方式下所需要花费的成本。
数据范围
前 30%的评测用例满足:1≤n≤20
前 60%的评测用例满足:1≤n≤100
所有评测用例都满足:1≤n≤1000,1≤m,k,d≤n^2,1≤xi,yi≤n
每个客户的订餐量不超过 10001000,每个客户所需要的餐都能被送到。
输入样例:
10 2 3 3
1 1
8 8
1 5 1
2 3 3
6 7 2
1 2
2 2
6 8
输出样例:
29
解题思路
求出分店到各个点的最短距离,根据最短距离求出最少成本,输出成本。
#include <iostream>
#include <queue>//队列头文件
#include <cstring>//memste 的头文件
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
bool g[N][N];// 地图:0:不可走,1:可走
int dist[N][N];//距离
int consumer[N * N][3];//用户地址和订餐的量
queue<PII> q;
int n, m, k, d;
void bfs()//广度优先遍历
{
int dx[] = {-1, 0 , 1, 0}, dy[] = {0, 1, 0,-1};//上下左右四个方向
while(q.size())
{
PII f = q.front();//取队头
q.pop();//出队
for(int i = 0; i < 4 ; i++)//向四个方向进行遍历
{
int x = f.first + dx[i], y = f.second + dy[i];
if(g[x][y] == 1)//如果能走过去
{
if(dist[x][y] > dist[f.first][f.second] + 1)//如果距离变小
{
dist[x][y] = dist[f.first][f.second] + 1;//更新距离
q.push({x, y});//并且入队
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);//输入量较大,此语句可以加快输出
memset(dist, 0x3f, sizeof(dist));//初始化dist,各个元素为int最大值
cin >> n >> m >> k >> d;
for(int i = 1; i <= n; i++)//初始化地图,全部为 1。地图从 g[1,1] 到 g[n,n],四周被 0 包围。这样再bfs时,可以不用做边界处理
{
for(int j = 1; j <= n; j++)
g[i][j] = 1;
}
while(m--)//分店
{
int x, y;
cin >> x >> y;
q.push({x, y});//分店入队列
dist[x][y] = 0;//分店的送餐距离为 0
}
for(int i = 0; i < k; i++)//保存客户信息
{
cin >> consumer[i][0] >> consumer[i][1] >> consumer[i][2];
}
while(d--)//不能走的地方为 0
{
int x, y;
cin >> x >> y;
g[x][y] = 0;
}
bfs();//广度优先遍历
long long res = 0;//保存结果
while(k > 0)//求出各个客户的成本
{
k--;
res += dist[consumer[k][0]][consumer[k][1]] * consumer[k][2];
}
cout << res;
}