宽度优先搜索--BFS

一、宽搜的一些注意点

(摘自挑战程序设计竞赛)

1.搜索顺序:从初始状态由近及远,即开始状态 -> 转移一次就可以到达的所有状态 -> 转移两次可以到达的所有状态 ->...

2.很容易用于求解最短路径、最小操作次数等等。

3.将已经访问过的状态标记起来。

4.搜索时先将初始状态加入队列,从队列最前端不断取出状态,把该状态可以转移到的状态中还没有被访问的部分加入队列,如此往复,直到队列取空或者找到答案。

//这样保证了是由近及远的顺序

二、CF920A Water The Garden

CF920A Water The Garden - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:

有一个长度为 n 的花园,一共有 k 个水龙头,分别在x_1,x_2​,…xk​ 的位置。第 t秒每个水龙头可以浇灌到它的前第 t-1 个方格和它后第 t-1 个方格,第 1 秒时则只浇灌自己所在的方格,问浇灌完花园的时间。

感谢@伏轩彤666 提供的翻译

1.初步想法

(好像有人说这不是bfs),就是模拟,主要是因为时间每次加一,就像宽搜是按照从开始状态由近及远的顺序去搜索一样,然后用vis标记每次浇到的方格(避免tot重复统计),用tot统计总数(当浇够了就输出t),所以bfs每次传的参数也是t。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int water[maxn],n,k;
int vis[maxn],tot;
void bfs(int t)
{
	for(int i=0;i<k;i++)//枚举水龙头 
	{
		if(water[i]-t+1>0&&water[i]-t+1<=n&&!vis[water[i]-t+1])
			vis[water[i]-t+1]=1,tot++;
		if(water[i]+t-1<=n&&water[i]+t-1>0&&!vis[water[i]+t-1])
			vis[water[i]+t-1]=1,tot++;
	}
	if(tot==n)
	{
		cout<<t<<endl;
		return; 
	}
	else
		bfs(t+1);
}

int main()
{
	int T;
	cin>>T;
	int ans=0; 
	while(T--)
	{
		cin>>n>>k;
		memset(vis,0,sizeof(vis));
		tot=0;
		for(int i=0;i<k;i++)
		{
			cin>>water[i];
		}
		bfs(1);
	}
	
	//cout<<ans;
}

 2.向外拓展的队列bfs

每一个水龙头都可以向外拓展,拓展1秒就相当在新位置上新装了个水龙头,每个水龙头向外喷水的距离都是1,然后每次把新水龙头push进队列且把时间++,把旧水龙头pop出去就成,用ans维护最大的时间。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int water[maxn],n,k;
int vis[maxn],tot;

struct node
{
	int s,time;
};
queue<node> q;
int ans=0;

int main()
{
	int T;
	cin>>T;
	int ans=0; 
	while(T--)
	{
		cin>>n>>k;
		memset(vis,0,sizeof(vis));
		for(int i=0;i<k;i++)
		{
			cin>>water[i];
			node temp;//临时设置一个点,用来暂时存水龙头,再放到队列里 
			temp.s=water[i]; 
			temp.time=1;
			q.push(temp);
			vis[water[i]]=1;//第一秒所有水龙头脚下的地方被标记 
		}
	 	while(!q.empty())
		{
	 		node x=q.front();
	 		q.pop();//把队列头拿出来看看
			ans=max(ans,x.time);//时间是取每个点用时的最大值
			//如果所有都vis了,就不会进入下面两个if 
			if(x.s-1>0&&x.s-1<=n&&!vis[x.s-1])//向左拓展,时间++,作为下一个考察点,放入队列 
			{
				node temp;
				temp.s=x.s-1;
				temp.time=x.time+1;
				q.push(temp);
				vis[x.s-1]=1;	
		 	} 
		 	if(x.s+1>0&&x.s+1<=n&&!vis[x.s+1])//向右拓展,时间++ 
			{
				node temp;
				temp.s=x.s+1;
				temp.time=x.time+1;
				q.push(temp);
				vis[x.s+1]=1;	
		 	} 
			
		}
	cout<<ans<<endl;
			ans=0;
	//cout<<ans;
	}
}

三、01迷宫

传送门:P1141 01迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

是这个样子的,处于一个连通块内的方格所可以走到的点的个数是相同的,算出来一个就把这个连通块内所有点都记上答案number(所以还要记录连通块走过的点past)。

另外就是放入队列的首先是查询的起始点,每次取出队首后,要把可以走到的点push进去(不超过边界且和取出来的点字符不一样)。

当然看过的点,我们就用book标记一下,就不再看了,反正都 已经是一个连通块里的人 或者是 别家连通块里的人,就不再看了。

本来想学一下用pair存点的,可是这个dev真的很失落。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
const int maxm=1e3+5; 
const int INF=1e8;

typedef pair<int,int> P;
int N,M;
char maze[maxn][maxm];//存迷宫 
int sx,sy,gx,gy;//起始点和终点的坐标
int book[1001][1001];//保存各个点所在的连通图,以及是非被处理过 
int step[maxn][maxn];//保存每个连通块的大小 
int vis[maxn];

int past[1000001][3];
//向四个方向移动的向量 
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1}; 

int bfs(int x,int y)
{
	int number=1;//可以到达的地点的个数
	past[number][1]=x;//记录走过的点 
	past[number][2]=y;
	int nowx,nowy; 
	queue<int> quex;
	queue<int> quey;
	book[x][y]=1;//标记起点,放入队列 
	quex.push(x);
	quey.push(y);
	while(quex.size()!=0)
	{
		quex.pop();
		quey.pop();
		for(int i=0;i<4;i++)
		{
			nowx=x+dx[i];
			nowy=y+dy[i];
			if(book[nowx][nowy]==0&&maze[x][y]!=maze[nowx][nowy]&&nowx>0&&nowx<=N&&nowy>0&&nowy<=N)
			{
				number++;
				past[number][1]=nowx;//记录走过的点 
				past[number][2]=nowy;
				book[nowx][nowy]=1;
				quex.push(nowx);
				quey.push(nowy);
			}
		}
		
		x=quex.front();
		y=quey.front();
	}
	for(int i=1;i<=number;i++)//再将每一个成员能到的点的个数分别记录下来!
	//下次见到这些点直接输出答案就行了,因为在连通块里,到的个数都一样哦~~
		step[past[i][1]][past[i][2]]=number;
		
	return number; 
}

int main()
{
	char c;
	cin>>N>>M;
	for(int i=1;i<=N;i++)
	for(int j=1;j<=N;j++)
	{
		cin>>maze[i][j];//存入地图 
	}
	for(int t=1;t<=M;t++)
	{
		int x,y;
		cin>>x>>y;
		if(book[x][y]==0)
		{
			bfs(x,y);
			cout<<step[x][y]<<endl;
		}
		else
			cout<<step[x][y]<<endl;
	}
	 
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序和三三总有一个能跑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值