BFS 2025/1/16
BFS

Basic 
主要特点:空间复杂度较高,基于队列 
经常用于求最优解的搜索题
经典模型:连通块,最短迷宫路径,曼哈顿距离
 
 
Question 01 [ACP2056 山峰与山谷] 
主体是广搜模板
难点在于如何判断当前联通块是山峰或山谷
考虑在广搜时进行维护
如果 BFS 检测到的区域不是在当前连通块
就判断其与当前连通块高度的大小关系 
进而判断当前连通块是山峰或山谷
注意要先判断大小关系再判断标记数组
(显而易见但一写就 WA ) 

Code
#include<bits/stdc++.h>
using namespace std;
const int N=1024,dx[10]={0,1,1,1,0,0,-1,-1,-1},dy[10]={0,1,0,-1,1,-1,1,0,-1};
int n,mp[N][N],top,bottom,ff,tt;
bool st[N][N],istop,isbottom;
struct Point{int x,y;}k[N*N];
void bfs(int sx,int sy){
	ff=1,tt=0;
	k[++tt]={sx,sy};
	st[sx][sy]=true;
	while(ff<=tt){
		int x=k[ff].x,y=k[ff++].y;
		for(int i=1;i<=8;i++){
			int xx=x+dx[i];
			int yy=y+dy[i];
			if(xx<1||yy<1||xx>n||yy>n)continue;
			if(mp[xx][yy]>mp[x][y]){
				istop=0;
				continue;
			}else if(mp[xx][yy]<mp[x][y]){
				isbottom=0;
				continue;
			}
			if(st[xx][yy])continue;
			st[xx][yy]=true;
			k[++tt]={xx,yy};
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&mp[i][j]);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
		if(st[i][j])continue;
		istop=1,isbottom=1;
		bfs(i,j);
		if(istop)++top;
		if(isbottom)++bottom;
	}
	printf("%d %d\n",top,bottom);
	return 0;
} 


Question 02 [ACP9084 武士风度的牛] 
BFS 模板题
重点调一下方向偏差值数组即可

Code
#include<bits/stdc++.h>
using namespace std;
const int N=160,dx[10]={0,2,2,1,1,-1,-1,-2,-2},dy[10]={0,1,-1,2,-2,2,-2,1,-1};
int n,m,ff,tt,fx,fy,sx,sy;
char mp[N][N];
bool st[N][N];
struct Point{int x,y,step;}k[N*N];
int bfs(){
	ff=1,tt=0;
	k[++tt]={sx,sy,0};
	st[sx][sy]=true;
	while(ff<=tt){
		int x=k[ff].x,y=k[ff].y,step=k[ff++].step+1;
		for(int i=1;i<=8;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx<1||yy<1||xx>n||yy>m)continue;
			if(st[xx][yy])continue;
			if(mp[xx][yy]=='*')continue;
			if(xx==fx&&yy==fy)return step;
			st[xx][yy]=true;
			k[++tt]={xx,yy,step};
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	swap(n,m);
	for(int i=1;i<=n;i++){
		scanf("%s",mp[i]+1);
		for(int j=1;j<=m;j++){
			if(mp[i][j]=='K')sx=i,sy=j;
			if(mp[i][j]=='H')fx=i,fy=j;
		}
	}
	printf("%d",bfs());
	return 0;
}


Question [ACP1912 迷宫]
题面就是裸的迷宫板子
思考如何输出路径
考虑记录每一状态前驱的队列下标
递归到达开始状态
在 return 的过程中输出即可

Code
#include<bits/stdc++.h>
using namespace std;
int mp[6][6],dx[6]={0,1,0,-1,0},dy[6]={0,0,1,0,-1},ff,tt;
bool st[6][6];
struct node{int x,y,previd;}k[30];
void print(int id){
	if(id==-1)return;
	print(k[id].previd);
	printf("(%d, %d)\n",k[id].x-1,k[id].y-1);
}
void dfs(int sx,int sy,int fx,int fy){
	ff=1,tt=0;
	k[++tt]={sx,sy,-1};
	st[sx][sy]=true;
	while(ff<=tt){
		int x=k[ff].x,y=k[ff++].y;
		for(int i=1;i<=4;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx<1||yy<1||xx>5||yy>5)continue;
			if(st[xx][yy])continue;
			if(mp[xx][yy])continue;
			st[xx][yy]=true;
			k[++tt]={xx,yy,ff-1};
			if(xx==fx&&yy==fy){
				print(tt);
				return;
			}
		}
	}
}
int main(){
	for(int i=1;i<=5;i++)for(int j=1;j<=5;j++)scanf("%d",&mp[i][j]);
	dfs(1,1,5,5);
	return 0;
}


Question 04 [ACP2520 矩阵距离]
曼哈顿距离模板
将 value 为一的位置入队列
标记 step 即可
ans 数组可与标记数组结合
 
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1012,dx[6]={0,1,0,0,-1},dy[6]={0,0,1,-1,0};
int n,m,mp[N][N],ff=1,tt=0,st[N][N];
struct Point{int x,y,step;}k[N*N];
int main(){
	scanf("%d%d",&n,&m);
	char input[N];
	for(int i=1;i<=n;i++){
		scanf("%s",&input);
		for(int j=1;j<=m;j++){
			mp[i][j]=input[j-1]-'0';
			if(mp[i][j])k[++tt]={i,j,0},st[i][j]=-1;
		}
	}
	while(ff<=tt){
		int x=k[ff].x,y=k[ff].y,step=k[ff++].step+1;
		for(int i=1;i<=4;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx<1||yy<1||xx>n||yy>m)continue;
			if(st[xx][yy]!=0)continue;
			st[xx][yy]=step;
			k[++tt]={xx,yy,step};
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)printf("%d ",(st[i][j]==-1)?0:st[i][j]);
		puts("");
	}
	return 0;
} 

Question 05[ACP2060 魔板] 
本题仍然是明确的 BFS
有明确的转移
难点在于记录状态
本题采用 int 进行存储
写出对应的 A,B,C 函数
 
Code
#include<bits/stdc++.h>
using namespace std;
const int ten[10]={1,10,100,1000,10000,100000,1000000,10000000};
struct node{int key,step,previd,way;}k[100000];
inline int A(int k){return (k/10000)+(k%10000)*10000;}
inline int B(int k){
	int ll=k/10000,rr=k%10000;
	ll=(ll%10)*1000+ll/10;
	rr=(rr%10)*1000+rr/10;
	return ll*10000+rr;
}
inline int C(int k){
	int a=k/ten[7]%10;
	int b=k/ten[6]%10;
	int c=k/ten[5]%10;
	int d=k/ten[4]%10;
	int e=k/ten[3]%10;
	int f=k/ten[2]%10;
	int g=k/ten[1]%10;
	int h=k/ten[0]%10;
	return a*ten[7]+f*ten[6]+b*ten[5]+d*ten[4]+e*ten[3]+g*ten[2]+c*ten[1]+h;
}
map<int,int> st;
char ans[2009];
int cnt=0;
void show(int id){
	if(k[id].previd==-1)return;
	show(k[id].previd);
	ans[++cnt]=(char)k[id].way;
}
void bfs(int sx,int fx){
	int ff=1,tt=0;
	k[++tt]={sx,0,-1,-1};
	st[sx]=114514;
	while(ff<=tt){
		int key=k[ff].key,previd=ff,step=k[ff++].step+1;
		int a=A(key),b=B(key),c=C(key);
		if(st[a]!=114514){
			st[a]=114514;
			k[++tt]={a,step,previd,(int)'A'};
			if(a==fx){
				printf("%d\n",step);
				show(tt);
				return;
			}
		}
		if(st[b]!=114514){
			st[b]=114514;
			k[++tt]={b,step,previd,(int)'B'};
			if(b==fx){
				printf("%d\n",step);
				show(tt);
				return;
			}
		}
		if(st[c]!=114514){
			st[c]=114514;
			k[++tt]={c,step,previd,(int)'C'};
			if(c==fx){
				printf("%d\n",step);
				show(tt);
				return;
			}
		}
	}
}
int main(){
	st.clear();
	int t=0,x,lable[10];
	for(int i=1;i<=8;i++)scanf("%d",&lable[i]);
	t=lable[1]*ten[7]+lable[2]*ten[6]+lable[3]*ten[5]+lable[4]*ten[4]+lable[8]*ten[3]+lable[7]*ten[2]+lable[6]*ten[1]+lable[5]*ten[0];
	if(t==12348765)puts("0"),exit(0);
	bfs(12348765,t);
	for(int i=1,it=1;i<=cnt;i++,it++){
		putchar(ans[i]);
		if(it==60)it=0,puts("");
	}
	return 0;
} 

Question 06 [ACP2059 电路维修]
毒瘤题! 
本题转移是向四角
重点是电力点其实是在格点上
所以向四周转移是有两种情况
一种是需要拧一下电路
另一种是不需要拧 
所以有时可能会导致 step 加一
也有可能不变
所以就不能保证先搜到的一定是答案
本题用到了 Dijstra 思想
用当前最小去松弛其转移点 
正常 Dijstra 是需要用 priority_queue 的
但在此题中会 TLE
我们要保证队列首位是最小的
而根据题意整个队列中最大和最小的元素最多差一
因此我们可以把扩展出来而没有增加 step 的元素直接放在队首
(因为原来队首的 step 就是最小的,而如果不加一 step 就不会变,仍然是最小的) 
反之若增加了 step 就放在队尾
所以我们需要使用双端队列
而如果一个元素已经是当前最小
根据 Dijstra 思想就无需再松弛了
所以本题的标记和判断 answer 都是对队首进行的
 
Code
#include<bits/stdc++.h>
using namespace std;
const int N=507,dx[7]={0,1,1,-1,-1},dy[7]={0,-1,1,-1,1};
char mp[N][N]={};
bool st[N][N];
int n,m;
struct node{
	int x,y,step;
};
bool able(int x,int y,int id){
	if(id==1)return mp[x+1][y]=='/';
	if(id==2)return mp[x+1][y+1]=='\\';
	if(id==3)return mp[x][y]=='\\';
	if(id==4)return mp[x][y+1]=='/';
	puts("Shit!");
	return 0;
}
deque<node> q;
int bfs(int sx,int sy){
	q.clear();//初始化 
	st[sx][sy]=true;
	q.push_front({sx,sy,0});
	while(!q.empty()){
		int x=q.front().x,y=q.front().y,step=q.front().step; 
		q.pop_front(); 
		st[x][y]=true;
		if(x==n&&y==m)return step;
		for(int i=1;i<=4;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx<0||yy<0||xx>n||yy>m)continue;
			if(st[xx][yy])continue;
			if(able(x,y,i))q.push_front({xx,yy,step});
			else q.push_back({xx,yy,step+1});
		}
	}
	puts("Fuck!");
	return 0;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)st[i][j]=false; 
		for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
		if((n+m)%2==1)puts("NO SOLUTION");
		else if(n+m==0)puts("0");
		else printf("%d\n",bfs(0,0));
	}
	return 0;
} 
posted on   2025ing  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示