BFS (多源,双端,双向)

广搜

Flood Fill

1.思想:
中文名为洪水填充。选择一个初始起点,从它扩散到相邻节点,一般有四连通和八连通。这算法可以在线性时间内,找到某个点所在的连通块
找水池

#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
#include <stdio.h>
#define x first
#define y second
using namespace std;
const int maxn = 1000+10;
int n,m;
char mp[maxn][maxn];
bool vis[maxn][maxn] = {false};
int dx[8] = {1,1,1,-1,-1,-1,0,0};
int dy[8] = {1,0,-1,1,0,-1,1,-1};
typedef pair<int,int> PII;

void bfs(int i, int j) {
	vis[i][j] = true;
	queue <PII> q;
	q.push((PII){i,j});

	while (q.size()) {
		PII t = q.front();
		q.pop();
		for (int k = 0; k < 8; ++k) {
			int ix = t.x + dx[k];
			int iy = t.y + dy[k];
			if (ix < 0 || iy < 0 || ix >= n || iy >= m) continue;
			if (vis[ix][iy]) continue;
			if (mp[ix][iy] == 'W') {
				q.push((PII){ix,iy});
				vis[ix][iy] = true;
			}

		}
	}
}

int main() {
	cin >> n >> m;
	int cnt = 0;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < m; ++j) cin >> mp[i][j];

	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < m; ++j) {
			if (mp[i][j] == 'W' && !vis[i][j]) {
				cnt++;
				bfs(i,j);
			}
		}
	}
	cout << cnt << endl;
	return 0;
}

多源BFS

多源BFS则是开始时就将多个起点入队,但相应的思想和普通的BFS并无太大差距。多源BFS可以用于有多个起点为条件的题中
[矩阵距离]

//多源BFS
#include <iostream>
#include <stdio.h>
#include <queue>
#define x first
#define y second 
#define PII pair<int,int>
using namespace std;

const int maxn = 1e3+10;
int n,m;
char mp[maxn][maxn];
int d[maxn][maxn];
bool vis[maxn][maxn]; 
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
queue<PII> q;

void bfs() {
	while (q.size()) {//队为空结束 
		PII t = q.front();//入队 
		q.pop();//出队 
		for (int i = 0; i < 4; ++i) {
			int ix = t.x + dx[i];
			int iy = t.y + dy[i];
			if (ix < 0 || iy < 0 || ix >= n || iy >= m) continue;
			if (vis[ix][iy]) continue;

			vis[ix][iy] = true;
			d[ix][iy] = d[t.x][t.y] + 1;//更新距离 
			q.push({ix,iy});
		}
	}
}

int main () {
	scanf("%d%d",&n,&m);
	for (int i = 0; i < n; ++i) cin >> mp[i];
		
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < m; ++j) {
			if (mp[i][j] == '1') {
				vis[i][j] = true;
				q.push({i,j});
			}
		}
	}
	bfs();
	for (int i = 0; i < n; ++i) {
		for  (int j = 0; j < m; ++j) cout << d[i][j] << " ";
		cout << endl;
	}
	return 0; 
}	 

优先队列BFS

当每次扩展的代价都是各自不同,即不再为1时,我们可以选择优先队列来进行广搜。这样就能取出当前代价最小的状态扩展。之后若再被取出,则可以直接忽略,不进行扩展。所以,优先队列BFS中每个状态只扩展一次,时间复杂度多了小顶堆的代价

rescue

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
const int N = 200+10;
struct node {
	int x;
	int y;
	int step;
	friend bool operator < (node x,node y) { //重载函数
	return x.step>y.step;
	}
};
	int n,m;
	char mp[N][N];
	bool vis[N][N];
	node ss; //标记Nina的位置
	int dx[4] = {1,-1,0,0}; //这题是四方向问题。
	int dy[4] = {0,0,1,-1};
	bool bfs() {
		priority_queue <node> q;
		q.push(ss);
		vis[ss.x][ss.y] = true;
		while (q.size()) {
			node now,next;
			now = q.top();
			q.pop();
			if (mp[now.x][now.y] == 'r') { //当Nina找到朋友时,我们就可以输出时间并且结束了
				cout << now.step << endl;
				return true;
			}
			for (int i = 0; i < 4; ++i) {
				next.x = now.x + dx[i];
				next.y = now.y + dy[i];
				if (next.x < 0 || next.x >= n || next.y < 0 || next.y >= m)
				continue;
				if (vis[next.x][next.y]) continue;
				if (mp[next.x][next.y] == '#') continue;
				if (mp[next.x][next.y] == 'x') { //当遇到警卫需杀掉他,所以时间需要加上2
				next.step = now.step + 2;
				vis[next.x][next.y] = true;
				q.push(next);
				} else {
				next.step = now.step + 1;
				vis[next.x][next.y] = true;
				q.push(next);
				}
			}
		}
			return false;
	}
	int main() {
		while (cin >> n >> m) {
			for (int i = 0; i < n; ++i) {
				for (int j = 0; j < m; ++j) {
					cin >> mp[i][j];
					if (mp[i][j] == 'a') { //找到Nina的位置,以她为起始点
						ss.x = i;
						ss.y = j;
						ss.step = 0;
					}
				}	
			}
			memset(vis,false,sizeof vis); //本题有多组输入,需要先初始化数组
			if (!bfs()) { //返回false代表朋友们未找到Nina
				cout << "Poor ANGEL has to stay in the prison all his life." <<
				endl;
			}
		}
		return 0;
	}

双端BFS

和普通BFS一样,起点入队,然后起点出队,从起点更新到其他点。
更新的两种情况
1.边权为0, 因为不增加消耗,所以加入到队首。
2.边权为其他。
这样我们能保证,任意时刻广搜队列中节点对应的距离值都有“两端性”和“单调性”,每个节点第一次被访问时,就能得到从左上角到该节点的最短距离。
小明的游戏

//双端队列 (适合于每一个点都可以走,找最少花费) 
#include <iostream>
#include <stdio.h>
#include <queue>
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int maxn = 500+10;
const int inf = 0x3fffffff;
int n,m;
int s1,s2,e1,e2;
char mp[maxn][maxn]; 
int d[maxn][maxn];//无穷远 
int vis[maxn][maxn];//变为0 
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
int bfs() {
	fill(vis[0], vis[0] + maxn * maxn, 0);
	fill(d[0], d[0] + maxn * maxn,inf);
	deque<PII> q;
	q.push_front({s1,s2});//起点输入到队首 
	d[s1][s2] = 0;//起点距离标记为0 
	vis[s1][s2] = 1;//表示访问过 

	while (q.size()) {
		PII t = q.front();
		q.pop_front();

		for (int i = 0; i < 4; ++i) {
			int ix = t.x + dx[i];
			int iy = t.y + dy[i];
			if (ix < 0 || iy < 0 || ix >= n || iy >= m) continue;
			if (vis[ix][iy]) continue;

			vis[ix][iy] = 1;
			int flag = 0;
			if (mp[ix][iy] != mp[t.x][t.y]) flag = 1;//不是相同的点则花费距离为1 
			if (d[ix][iy] > d[t.x][t.y] + flag) {
				d[ix][iy] = d[t.x][t.y] + flag;//更新距离 
			}
			if (flag) q.push_back({ix,iy});//不相同则放入到队尾 
			else q.push_front({ix,iy}) ;//相同放入队首 
		}
	}

}
int main() {
	while (cin >> n >> m && n != 0 && m != 0) {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) cin >> mp[i][j];
		}
		cin >> s1 >> s2 >> e1 >> e2;
		bfs();
		cout << d[e1][e2] << endl;//输出距离 
	}

	return 0;
}

双向BFS

双向BFS适用于知道起点和终点的状态下使用,从起点和终点两个方向开始进行搜索,可以非常大的提高单个BFS的搜索效率
同样,实现也是通过队列的方式,可以设置两个队列,一个队列保存从起点开始搜索的状态,另一个队列用来保存从终点开始搜索的状态,如果某一个状态下出现相交的情况,那么就出现了答案
走迷宫
AC代码

//双向BFS 
#include <iostream>
#include <queue>
#define PII pair<int, int>
#define x first
#define y second

using namespace std;

const int N = 100+10;

int n,m;
char mp[N][N];
int dis[N][N];
int st[N][N];//标记,两个队列标记不一样 
int ans = 0;
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};

void bfs() {
	queue<PII> q, p;
	q.push({1,1});//输入起点 
	p.push({n,m});//输入终点 
	dis[1][1] = 1;
	dis[n][m] = 1;
	st[1][1] = 2;//走过的标记为2 
	st[n][m] = 3;//走过的标记为3 
	while (q.size() && p.size()) {
		if (q.size() > p.size()) {
			PII t = p.front();
			p.pop();
			for (int i = 0; i < 4; ++i) {
				int ix = t.x + dx[i];
				int iy = t.y + dy[i];

				if (ix <= 0 || iy <= 0 || ix > n || iy > m) continue;
				if (mp[ix][iy] == '#') continue;


				if (!dis[ix][iy]) {
					st[ix][iy] = st[t.x][t.y];//标记为相同坐标 
					dis[ix][iy] = dis[t.x][t.y] + 1;
					p.push({ix,iy});
				} else {
					if(st[ix][iy] + st[t.x][t.y] == 5) {
						ans = dis[ix][iy] + dis[t.x][t.y];
						return;
					}
				}
			}
		} else {
			PII t = q.front();
			q.pop();
			for (int i = 0; i < 4; ++i) {
				int ix = t.x + dx[i];
				int iy = t.y + dy[i];

				if (ix <= 0 || iy <= 0 || ix > n || iy > m) continue;
				if (mp[ix][iy] == '#') continue;

				if (!dis[ix][iy]) {
					st[ix][iy] = st[t.x][t.y];
					dis[ix][iy] = dis[t.x][t.y] + 1;
					q.push({ix,iy});
				} else {
					if(st[ix][iy] + st[t.x][t.y] == 5) {//如果下一个被访问过 ,则判断是否结束 
						ans = dis[ix][iy] + dis[t.x][t.y];
						return;
					}
				}
			}
		}
	}
}

int main() {
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			cin >> mp[i][j];
	bfs();
	cout << ans << "\n";
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值