[AcWing]BFS相关
AcWing: BFS相关
背景知识:有向图
离散数学图论中的概念,由点和边组成图,其中图中所有边都是带有方向的图称为有向图。
在算法题中,我们可以用邻接矩阵和邻接表(推荐)存储有向图。

邻接表即类似于哈希表的拉链法,用链表来解决冲突问题。并且其中的链表顺序并不重要(存储相邻点),对于解决bfs问题来说。
宽搜框架

例题一:图中点的层次

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
//两层节点体系:一层为idx;一层为有向图中点的编号存在e[N]中
int n, m;
int h[N], e[N], ne[N], idx; //h[N]为哈希表,e[N]用来存节点指向的节点在有向图中的编号,ne[N]为领接表上的next,idx为当前用到的节点数
int d[N], q[N]; //q[N]为队列存储bfs搜索过程,d[N]标识点是否被搜索过,记录到点1的距离
void add(int a, int b) //将边a指向b加入邻接表中
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int bfs()
{
int hh = 0, tt = 0;//初始化queue队列
q[0] = 1;
memset(d, -1, sizeof d);
d[1] = 0;
while(hh <= tt)
{
int t = q[hh ++];//bfs用队列弹出跳到下一层搜
for(int i = h[t]; i != -1; i = ne[i])//bfs一般都是用for在当前层搜
{
int j = e[i];
if(d[j] == -1)
{
d[j] = d[t] + 1;//距离加1,可以得到点1到所有点的距离,存储在数组d[N]中,也完成了这一层的搜索
//对当前层来说t的值是固定的,所以是按层搜索,下一层t的值会变为队头的元素值,之前的队头已经出队了
q[++tt] = j;
}
}
}
return d[n];
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);//初始化h[N]哈希表,-1代表指向为空null
for(int i = 0; i < m; i ++) //存入有向图
{
int a, b;
cin >> a >> b;
add(a, b);
}
cout << bfs() << endl;
return 0;
}
例题二:拓扑序列
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];//d[N]记录有向图中编号为i的点的入度
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool topsort()
{
int hh = 0, tt = -1;//tt表示队列中加入的点数-1
for(int i = 1; i <= n; i ++)//把入度为0的点加入队列中
if(!d[i]) q[++tt] = i;
while(hh <= tt)
{
int t = q[hh++];//用弹出队头来搜索下一次层
for(int i = h[t]; i != -1; i = ne[i])//用for来进行当前层搜索
{
int j = e[i];
d[j] --; //以入度减1表示该点搜过了,看习题课寻求答疑
if(d[j] == 0) q[++tt] = j; //将入度为0的点加入队列
}
}
return tt == n - 1;//如果为true,代表n个点全部加入了队列中
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++)
{
int a, b;
cin >> a >> b;
add(a, b);
d[b] ++;//顺带记录入度
}
if(topsort())
{
for(int i = 0; i < n; i++) printf("%d ", q[i]);
puts("");
}
else puts("-1");
return 0;
}
例题三、走迷宫

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int g[N][N];//存储地图的数组
int d[N][N];//存储距离的数组
int iuse[N][N];//存储是否用过的节点数组
int n, m;
PII q[N * N];
int bfs()
{
int hh = 0, tt = 0;
q[0] = {0,0};
memset(d, -1, sizeof d);
memset(iuse, -1, sizeof iuse);
d[0][0] = 0;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
while(hh <= tt)
{
PII t = q[hh ++];//利用弹出队列来完成进行下一层搜索
for(int i = 0; i < 4; i++)//利用for来完成当前层搜索
{
int x = t.first + dx[i], y = t.second + dy[i];
if(g[x][y] == 0 && iuse[x][y] == -1 && x >= 0 && x < n && y >= 0 && y < m)//是否触碰地图边界?是否在障碍区域?是否被用过
{
iuse[x][y] = 1;
d[x][y] = d[t.first][t.second] + 1;
q[++ tt] = {x, y};
}
}
}
return d[n - 1][m - 1];
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin >> g[i][j];
cout << bfs() << endl;
return 0;
}
例题四、八数码

//用字符串哈希表存储过程中产生的中间值,避免使用char二重数组存储带来大量的空间浪费
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
int bfs(string state)
{
queue<string> q;
unordered_map<string, int> d;
q.push(state);
d[state] = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
string end = "12345678x";
while (q.size())
{
auto t = q.front();
q.pop();
if (t == end) return d[t];
int distance = d[t];
int k = t.find('x');
int x = k / 3, y = k % 3;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3)
{
swap(t[a * 3 + b], t[k]);
if (!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
swap(t[a * 3 + b], t[k]);
}
}
}
return -1;
}
int main()
{
char s[2];
string state;
for (int i = 0; i < 9; i ++ )
{
cin >> s;
state += *s;
}
cout << bfs(state) << endl;
return 0;
}