文章目录
池塘计数(flood fill)
问题描述
解题思路
flood fill模板题,掌握基本的bfs就可以上手做了,具体思路和解释见代码中注释
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1005;
char w[N][N]; //读入池塘的字符串
bool st[N][N]; // st数组用于判重,表示该点已经走过
int res = 0, n, m;
//八个方向的方向偏移量
int dx[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[8] = {-1, 0, 1, 1, 1, 0, -1, -1};
queue<PII> q;
void bfs(int x, int y) //每次BFS会搜索一块连通区域
{
q.push({x, y});
st[x][y] = true;
while (q.size())
{
int xx = q.front().x, yy = q.front().y;
q.pop();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
{
int a = dx[i] + xx, b = dy[i] + yy;
if (a < 1 || b < 1 || a > n || b > m)
continue; //若越界,则不符合要求,返回
if (st[a][b])
continue; //如果已经搜索过了,则不符要求,返回
if (w[a][b] == '.')
continue; //如果是海洋,则不符要求,返回
q.push({a, b}); //都满足要求了,则将该点入队
st[a][b] = true; //同时标记为已经搜索过了
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (w[i][j] == 'W' && !st[i][j])
bfs(i, j), res++; //每搜索一次连通区域,答案数量+1
}
cout << res << endl;
return 0;
}
城堡问题(flood fill)
问题描述
解题思路
也是经典的flood fill算法问题,只不过稍微增加了一层包装,关键在于每个数字的意义是什么,具体解释见下图
图片转自
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1005;
int w[N][N];
bool st[N][N];
int res = 0, n, m;
int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
queue<PII> q;
int bfs(int x, int y)
{
int cnt = 0;
q.push({x, y});
st[x][y] = true;
while (q.size())
{
int xx = q.front().x, yy = q.front().y;
q.pop();
cnt++;
for (int i = 0; i < 4; i++)
{
int a = dx[i] + xx, b = dy[i] + yy;
if (a < 1 || b < 1 || a > n || b > m)
continue; //判断是否出界
if (st[a][b])
continue; //已经被搜索过了
if (w[xx][yy] >> i & 1)
continue; //此处是关键:判断当前点的周围的点是否是墙,如果是墙,则返回
q.push({a, b});
st[a][b] = true;
}
}
return cnt;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
int area = 0, res = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (!st[i][j])
{
area = max(area, bfs(i, j));
res++;
}
}
cout << res << endl
<< area;
return 0;
}
山峰和山谷(flood fill)
问题描述
解题思路
还是flood fill的基本模型,问题转换为,搜索连通区域是值为相等的区域,同时,需要判断连通块中周围的点是否比他高还是比他低由此判断出它是山峰还是山谷
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
int n;
int w[N][N];
bool st[N][N];
int dx[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[8] = {-1, 0, 1, 1, 1, 0, -1, -1};
queue<PII> q;
//相比其他flood fill的题目,本题就是多了两个参数,用于判断是一个连通块周围是否存在比他高的点或者低的点
void bfs(int xx, int yy, bool &high, bool &low)
{
q.push({xx, yy});
st[xx][yy] = true;
while (q.size())
{
int x = q.front().x, y = q.front().y;
q.pop();
for (int i = 0; i < 8; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 1 || b < 1 || a > n || b > n)
continue;
if (w[a][b] != w[x][y])
{
if (w[a][b] > w[x][y])
high = true;
else
low = true;
}
else if (!st[a][b])
{
q.push({a, b});
st[a][b] = true;
}
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
cin >> w[i][j];
}
int peak = 0, valley = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (!st[i][j])
{
bool high = false, low = false;
bfs(i, j, high, low);
if (!high)
peak++;
if (!low)
valley++;
}
}
cout << peak << " " << valley;
return 0;
}
迷宫问题(最短路模型)
问题描述
解题思路
相比于前面几题,本题的特点是需要输出最短路的路径,因此,在bfs的过程中,需要采用pre数组记录每一个状态的前驱状态,最后按顺序输出即可。因为是要按正序输出,所以我们从终点向起点做bfs,这样最后按序输出的pre数组即为答案。
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
queue<PII> q;
int n;
int w[N][N];
PII pre[N][N]; //相比于前面几题,pre数组用于记录一个状态的前驱状态
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void bfs()
{
memset(pre, -1, sizeof pre);
pre[n - 1][n - 1] = {0, 0}; //从终点向起点做bfs
q.push({n - 1, n - 1});
while (q.size())
{
int x = q.front().x, y = q.front().y;
PII temp = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= n)
continue;
if (pre[a][b].x != -1)
continue;
if (w[a][b])
continue;
q.push({a, b});
pre[a][b] = temp; //记录前驱
}
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> w[i][j];
bfs();
PII end(0, 0);
while (true)
{
//在输出时,从起点开始输出,然后不停地寻找前驱,直到找到(n-1,n-1)这个点
cout << end.x << " " << end.y << endl;
if (end.x == n - 1 && end.y == n - 1)
break;
end = pre[end.x][end.y];
}
return 0;
}
武士风度的牛(最短路模型)
问题描述
解题思路
和前面的题相比,本题只是多了一个dist数组
,用于记录从起点到达当前位置的距离,其余的都是广搜的模板
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 200;
int n, m;
char g[N][N];
int dist[N][N];//dist数组表示从起点到当前点的最短距离
int gx, gy, ox, oy;
int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int res = 0;
queue<PII> q;
void bfs(int ox, int oy)
{
memset(dist, -1, sizeof dist);
q.push({ox, oy});
dist[ox][oy] = 0;
while (q.size())
{
int x = q.front().x, y = q.front().y;
q.pop();
int a, b;
for (int i = 0; i < 8; i++)
{
a = x + dx[i], b = y + dy[i];
if (a < 1 || b < 1 || a > n || b > m)
continue;
if (dist[a][b] != -1)
continue;
if (g[a][b] == '*')
continue;
dist[a][b] = dist[x][y] + 1;
q.push({a, b});
}
}
}
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
cin >> g[i][j];
if (g[i][j] == 'K')
ox = i, oy = j;
if (g[i][j] == 'H')
gx = i, gy = j;
}
bfs(ox, oy);
cout << dist[gx][gy];
return 0;
}
抓住那头牛(最短路模型)
问题描述
解题思路
一维线段
上的bfs,会出现三个方向的状态偏移。
需要注意的是每个点并非只能遍历一次,具体见注释\
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2e5 + 5; //这里在定义最大N的时候一定要注意bfs会搜索到两倍空间,所以需要开两倍
queue<int> q;
int d[N];
int n, k;
void bfs(int m)
{
q.push(m);
while (q.size())
{
int x = q.front();
q.pop();
int c[3] = {-1, 1, x}; //方向偏移量
for (int i = 0; i < 3; i++)
{
int a = x + c[i];
//若当前点<0时,此时,只能+1往前走,会做无用功
//若当前点>2k时,说明肯定是经过*2来到此状态的,那么也是相当于无用功
if (a <= 0 || a > k * 2)
continue;
if (d[a] > d[x] + 1 || d[a] == -1) //每个点并非只能经过一次,每次经过一个点时,要么没有经过,要么可以被更新到更小
{
d[a] = d[x] + 1;
q.push(a);
}
}
if (x == k)
return;
}
}
int main()
{
cin >> n >> k;
memset(d, -1, sizeof d);
d[n] = 0;
bfs(n);
if (n > k) //若当前点在目标点右侧,则最优方案就是不停往前走
{
cout << n - k;
return 0;
}
cout << d[k] << endl;
return 0;
}
矩阵距离(多源BFS)
问题描述
解题思路
容易将题目抽象为求出矩阵上的每个点到达所有值为1的点的距离的最小值。我们可以将所有值为1的点初始化为起点,易得最小值为0,同时我们虚拟出一个源点
,源点到达各起点的距离为-1。题目进一步抽象为求出虚拟源点到达各个点的最短距离,此时就转换为了普通的最短路模型了
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n, m;
char w[N][N];
int dist[N][N]; // dist数组代表的是矩阵上的每个点到达所有1这个点的距离中的最小值
int st[N][N];
queue<PII> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void bfs()
{
//和前面几题相比,只需要将所有起点初始加入到队列中
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (w[i][j] == '1')
{
dist[i][j] = 0;
st[i][j] = true;
q.push({i, j});
}
}
while (q.size())
{
int x = q.front().x, y = q.front().y;
q.pop();
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (st[a][b])
continue;
if (a < 1 || b < 1 || a > n || b > m)
continue;
dist[a][b] = dist[x][y] + 1;
q.push({a, b});
st[a][b] = true;
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
bfs();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
cout << dist[i][j] << " ";
cout << endl;
}
return 0;
}