广搜
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";
}