参考课程是我高中信息竞赛邱老师的课程。
【14-4 搜索:广度优先搜索1】 https://www.bilibili.com/video/BV1n2421N78a/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca
【14-5 搜索:广度优先搜索2】 https://www.bilibili.com/video/BV1fD421j737/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca
【14-4、5 BFS习题讲解】 https://www.bilibili.com/video/BV1qM4m1X73k/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca
需要完整板子可以去我资料获取,我设定的0积分,如果还要花钱去B站私我。
BFS(最短路问题)
BFS的链式前向星实现
这里遍历邻接节点的目的不是去搜索,而是放到队列中,只搜索队列最前面的


模板 BFS搜索最短路问题
注意!!stp一定要加f前缀!!!!!!!!!
不要被条件约束住,查询问题可以搜完再查,没必要搜的时候查!!!
多次查询一定要清空vis啊!!!
用swap清空queue!
cpp
queue<Node> q;
memset(vis,0,sizeof(vis));
memset(maze,-1,sizeof(maze));
queue<Node> empty_q;
swap(q,empty_q);
注意,超过2000*2000的数组不可以memset初始化,不然会TLE!
多次查询这种题只需要初始化vis即可,每次查询不一致即可。maze会被覆盖,pre_x\pre_y\pre_dir也会被覆盖。
这是一个极好的模板,Node用于记录关键信息,这道题用的是长度,下一道题用是坐标,Node的内容就是x和y。

注意!! 由于使用了Node结构体存储u和长度len。
因此后续访问u都有用f。
cpp
#include<iostream>
#include<queue>
using namespace std;
bool vis[100005]={false};
int N,M;
int fst[10005]={0};
struct Edge{
int v,next;
};
struct Node{
int u,len;
Node(int u1=1,int length=0){
u=u1;len=length;
}
};
Edge E[200005];
int L=1;
void addEdge(int u,int v){
E[L].v=v;
E[L].next=fst[u];
fst[u]=L++;
}
queue<Node> q;
int main(){
cin>>N>>M;
for(int i=0;i<M;i++){
int u,v;
cin>>u>>v;
addEdge(u,v);
addEdge(v,u);
}
q.push(Node(1,0));
vis[1]=true;
while(!q.empty()){
Node f=q.front();
//cout<<u<<" ";
if(f.u==N){
cout<<f.len<<endl;
return 0;
}
q.pop();
for(int p=fst[f.u];p;p=E[p].next){
int v=E[p].v;
if(!vis[v]){
vis[v]=true;
q.push(Node(v,f.len+1));
}
}
}
}
BFS 二维迷宫 B3625(提前截止)
https://www.luogu.com.cn/problem/B3625

cpp
#include<bits/stdc++.h>
using namespace std;
bool vis[105][105];
int maze[105][105];
int h[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int N,M;
struct Node{
int x,y;
Node(int x1=1,int y1=1){
x=x1;y=y1;
}
};
queue<Node> q;
int main(){
cin>>N>>M;
char temp;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
cin>>temp;
if(temp=='#'){
maze[i][j]=0;
}else{
maze[i][j]=1;
}
}
}
q.push(Node(1,1));
vis[1][1]=true;
while(!q.empty()){
Node f=q.front();
if(f.x==N && f.y==M){
cout<<"Yes"<<endl;
return 0;
}
q.pop();
//接下来要探索这个迷宫
//上下左右+约束
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=N&&ny<=M&&!vis[nx][ny]&&maze[nx][ny]){
vis[nx][ny]=true;
q.push(Node(nx,ny));
}
}
}
cout<<"No"<<endl;
}
BFS 二维迷宫 P1443(非提前截止)
https://www.luogu.com.cn/problem/P1443
这道题要求遍历所有情况,因此不添加提前截止代码。

cpp
#include<bits/stdc++.h>
using namespace std;
int N,M,x,y;
int h[8][2]={{-1,2},{1,2},{-2,1},{2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
int vis[405][405];
int maze[405][405];
void ptnmaze(){
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
cout<<maze[i][j]<<" ";
}
cout<<endl;
}
}
//最少步数-》最短路问题
struct Node{
int x,y,stp;
Node(int x1=0,int y1=0,int stp1=0){
x=x1;y=y1;stp=stp1;
}
};
queue<Node> q;
int main(){
cin>>N>>M>>x>>y;
memset(maze,-1,sizeof(maze));
q.push(Node(x,y,0));
vis[x][y]=true;
maze[x][y]=0;
while(!q.empty()){
Node f=q.front();
//终止条件是什么
// if(f.stp>=N*M){
// //一个过限条件
// ptnmaze();
// return 0;
// }
q.pop();
int nx,ny;
for(int i=0;i<8;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=N&&ny<=M&&!vis[nx][ny]){
vis[nx][ny]=true;
maze[nx][ny]=f.stp+1;
q.push(Node(nx,ny,f.stp+1));
}
}
}
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
cout<<maze[i][j]<<" ";
}
cout<<endl;
}
}
练习1 T343500 细胞问题
https://www.luogu.com.cn/problem/T434500

我犯了2个错误。
如果从1开始存,那么探索通过条件应该是if(nx<=N && ny<=M)
只要当前没有vis且maze为0就说明发现了新细胞。
cpp
#include<bits/stdc++.h>
using namespace std;
int N,M;
int maze[105][105];
int vis[105][105];
int h[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
//对所有点开广搜 每个点都看看四周 如果有菲林就标记 没有截止条件
struct Node{
int x,y;
Node(int x1=0,int y1=0){
x=x1;y=y1;
}
};
queue<Node> q;
int main(){
char temp;
cin>>N>>M;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
cin>>temp;
maze[i][j]=temp-'0';
}
}
int ans=0;
memset(vis,0,sizeof(vis));
int flag=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
if(maze[i][j]&&!vis[i][j]){
ans++;
q.push(Node(i,j));
vis[i][j]=true;
while(!q.empty()){
Node f=q.front();
q.pop();
int nx,ny;
for(int k=0;k<4;k++){
nx=f.x+h[k][0];ny=f.y+h[k][1];
if(nx&&ny&&nx<=N&&ny<=M&&!vis[nx][ny]&&maze[nx][ny]){
vis[nx][ny]=true;
flag=1;
q.push(Node(nx,ny));
}
}
}
}
}
}
cout<<ans<<endl;
}
练习2 P1746 离开中山路
https://www.luogu.com.cn/problem/P1746

我犯的错:记录最短路要用Node内变量,不能写外面。
cpp
#include<bits/stdc++.h>
using namespace std;
//提前截止的BFS最短路
int maze[1005][1005];
bool vis[1005][1005];
int h[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int n;
struct Node{
int x,y,stp;
Node(int x1=0,int y1=0,int stp1=0){
x=x1;y=y1;stp=stp1;
}
};
queue<Node> q;
int main(){
int x1,y1,x2,y2;
cin>>n;
string s;
for(int i=1;i<=n;i++){
cin>>s;
char temp;
//int p;
//cin>>p;
for(int j=1;j<=n;j++){
//int temp=p%(10^j);
temp=s[j-1];
maze[i][j]=temp-'0';
}
}
cin>>x1>>y1>>x2>>y2;
q.push(Node(x1,y1,0));
vis[x1][y1]=true;
while(!q.empty()){
Node f=q.front();
if(f.x==x2&&f.y==y2){
cout<<f.stp<<endl;
return 0;
}
q.pop();
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=n&&ny<=n&&!vis[nx][ny]&&!maze[nx][ny]){
vis[nx][ny]=true;
q.push(Node(nx,ny,f.stp+1));
}
}
}
cout<<-1<<endl;
}
多源DFS(一次塞几个Node进q) 练习3 血色先锋队 P1332
https://www.luogu.com.cn/problem/P1332

错误示范:
初次思路是先记下来所有领袖的位置,然后搜索到的时候记录下来。
但是这样复杂度太高了,而且要额外的映射去记录输入和最短的关系,而b的数量级很大,不宜用二维数组,就死了。

又忘了f.stp 我是傻逼

这道题正解是全部搜索完,得到每个点的最短值,然后拿这张搜索完的图去查找!
cpp
//多源的扩散 多源的BFS 就是直接往q中塞好几个源
#include<bits/stdc++.h>
using namespace std;
int maze[505][505];
bool vis[505][505];
int h[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
struct Node{
int x,y,stp;
Node(int x1=0,int y1=0,int stp1=0){
x=x1;y=y1;stp=stp1;
}
};
queue<Node> q;
int n,m,a,b;
int main(){
cin>>n>>m>>a>>b;
for(int i=0;i<a;i++){
int x,y;
cin>>x>>y;
q.push(Node(x,y,0));
vis[x][y]=true;
maze[x][y]=0;
}
while(!q.empty()){
Node f=q.front();
maze[f.x][f.y]=f.stp;
q.pop();
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=n&&ny<=m&&!vis[nx][ny]){
vis[nx][ny]=true;
q.push(Node(nx,ny,f.stp+1));
}
}
}
for(int i=0;i<b;i++){
int x,y;
cin>>x>>y;
cout<<maze[x][y]<<endl;
}
}
带时间的DFS P3395 用swap清空queue
重点在于如何情况queue做初始化
cpp
queue<Node> q;
memset(vis,0,sizeof(vis));
memset(maze,-1,sizeof(maze));
queue<Node> empty_q;
swap(q,empty_q);
https://www.luogu.com.cn/problem/P3395

cpp
#include<bits/stdc++.h>
using namespace std;
//探索一个下一层节点时会修改一次maze而已
//引入新的维度,把maze做成3维的,第三维是时间,一开始设置所有的第三维为-1,然后每次有输入就拉进去,然后if检查的时候要么nxny的第三维为-1,要么当前的时间点,也就是f.stp<=第三维
int T;
int n;
int maze[1005][1005];
int vis[1005][1005];
int h[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
struct Node{
int x,y,t;
Node(int x1=0,int y1=0,int t1=-1){
x=x1;y=y1;t=t1;
}
};
queue<Node> q;
int main(){
cin>>T;
while(T--){
memset(vis,0,sizeof(vis));
memset(maze,-1,sizeof(maze));
queue<Node> empty_q;
swap(q, empty_q);
cin>>n;
int flag=0;
for(int i=1;i<=2*n-2;i++){
int x,y;
cin>>x>>y;
maze[x][y]=i;
}
q.push(Node(1,1,0));
vis[1][1]=true;
while(!q.empty()){
Node f=q.front();
if(f.x==n&&f.y==n){
flag=1;
break;
}
q.pop();
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=n&&ny<=n&&!vis[nx][ny]&&(f.t<=maze[nx][ny] || maze[nx][ny]==-1)){
vis[nx][ny]=true;
q.push(Node(nx,ny,f.t+1));
}
}
}
if(flag){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
}
需要输出路径的DFS P10234
https://www.luogu.com.cn/problem/P10234


必须开三个数组,存该点的前一个x,该点的前一个y,该点的前一个点到当前的方向。都是二维向一位的映射。
注意,超过2000*2000的数组不可以memset初始化,不然会TLE!
多次查询这种题只需要初始化vis即可,每次查询不一致即可。maze会被覆盖,pre_x\pre_y\pre_dir也会被覆盖。
cpp
// pre_x/y 记录前驱节点的坐标,pre_dir 记录从前驱走到当前的方向
int pre_x[2005][2005], pre_y[2005][2005];
char pre_dir[2005][2005];
char dir_char[4] = {'U', 'D', 'L', 'R'};
pre_x[nx][ny] = f.x; // 记下:我是从 f.x 走过来的
pre_y[nx][ny] = f.y; // 记下:我是从 f.y 走过来的
pre_dir[nx][ny] = dir_char[i];
路径递归输出模板
cpp
string path="";
int cx=n,cy=m;//从终点开始
while(pre_x[cx][cy]!=-1){//如果cxcy这个点的前驱不是-1的话就往前遍历
path+=pre_dir[cx][cy];//将上一步步骤存进path
int tx=pre_x[cx][cy];
int ty=pre_y[cx][cy];
cx=tx;//向前迭代
cy=ty;
}
//由于是向前迭代,因此要reverse一下字符串
reverse(path.begin(),path.end());
cout<<path<<endl;
cpp
#include<bits/stdc++.h>
using namespace std;
int maze[2005][2005];
int vis[2005][2005];
int n,m,T;
int h[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int pre_x[2005][2005],pre_y[2005][2005];
int pre_dir[2005][2005];
char dir_char[4]={'U','D','L','R'};
struct Node{
int x,y,stp;
Node(int x1=0,int y1=0,int stp1=0){
x=x1;y=y1;stp=stp1;
}
};
queue<Node> q;
int main(){
cin>>T;
while(T--){
memset(vis,0,sizeof(vis));
queue<Node> empty;
swap(q,empty);
cin>>n>>m;
int flag=0;
int min_len=0;
char temp;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>temp;
maze[i][j]=temp-'0';
}
}
q.push(Node(1,1,0));
vis[1][1]=true;
pre_x[1][1]=-1;pre_y[1][1]=-1;
while(!q.empty()){
Node f=q.front();
if(f.x==n&&f.y==m){//如果找到终点
flag=1;
min_len=f.stp;
break;
}
q.pop();
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=n&&ny<=m&&!vis[nx][ny]&&maze[f.x][f.y]!=maze[nx][ny]){
vis[nx][ny]=true;
pre_x[nx][ny]=f.x;
pre_y[nx][ny]=f.y;
pre_dir[nx][ny]=dir_char[i];
q.push(Node(nx,ny,f.stp+1));
}
}
}
if(flag){
cout<<min_len<<endl;
string path="";
int cx=n,cy=m;//从终点开始
while(pre_x[cx][cy]!=-1){//如果cxcy这个点的前驱不是-1的话就往前遍历
path+=pre_dir[cx][cy];//将上一步步骤存进path
int tx=pre_x[cx][cy];
int ty=pre_y[cx][cy];
cx=tx;//向前迭代
cy=ty;
}
//由于是向前迭代,因此要reverse一下字符串
reverse(path.begin(),path.end());
cout<<path<<endl;
}else{
cout<<-1<<endl;
}
}
}
我是傻逼的解法,解不了
cpp
#include<bits/stdc++.h>
using namespace std;
//带递归路径的BFS 在Node内置一个记录上一个点的pre即可。
//用一个char类型的deque记录UDLR,然后直接倒着取出来
int maze[2005][2005];
int vis[2005][2005];
int n,m,T;
int h[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
const map<int,char> ma={{0,'U'},{1,'D'},{2,'L'},{3,'R'}};
deque<char> dc;
struct Node{
int x,y,stp;
Node(int x1=0,int y1=0,int stp1=0){
x=x1;y=y1;stp=stp1;
}
};
queue<Node> q;
void ptnvers(){
for(auto it=dc.rbegin();it!=dc.rend();it++){
cout<<*it<<endl;
}
return;
}
int main(){
cin>>T;
while(T--){
memset(vis,0,sizeof(vis));
memset(maze,-1,sizeof(maze));
dc.clear();
queue<Node> empty;
swap(q,empty);
cin>>n>>m;
int flag=0;
int min_len=INT_MAX;
char temp;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>temp;
maze[i][j]=temp-'0';
}
}
q.push(Node(1,1,0));
vis[1][1]=true;
while(!q.empty()){
Node f=q.front();
if(f.x==n&&f.y==m){//如果找到终点
flag=1;
min_len=f.stp;
break;
}
int nx,ny;
for(int i=0;i<4;i++){
nx=f.x+h[i][0];ny=f.y+h[i][1];
if(nx&&ny&&nx<=n&&ny<=m&&!vis[nx][ny]&&maze[f.x][f.y]!=maze[nx][ny]){
vis[nx][ny]=true;
auto it=ma.find(i);
dc.push_back(it->first);
q.push(Node(nx,ny,f.stp+1));
}
}
}
if(flag){
cout<<min_len<<endl;
ptnvers();
}else{
cout<<-1<<endl;
}
}
}
DFS VS BFS
