最近一直跟着y总在刷蓝桥杯的课,借此我来对里面涉及到的Flood Fill算法做一个我目前接触到的所有的flood fill算法的总结
总
首先,这本质上是一个搜索算法。他的作用是对一个图(目前我用到的都是二维数组的图,不是图论的图)的所有的连通块进行操作
它是可以从其中一个点出发,扩展与这个点相邻的所有点
也就是说,给定连通块的所有的点,都可以把这个连通块上所有的点全部搜到
那么我对每一步进行讲解,并对每一个例题给出代码,这里只讲解bfs的写法
int bfs(PII start){
queue<PII> q; // 宽搜用的队列
q.push(start); // 宽搜入队
memset(vis, 0, sizeof(vis)); // 对数组进行清空,表示所有点暂时还没有到达过
vis[start.x][start.y] = 1; // 起点是肯定来过的
int dx[4] = {-1,0,1,0}; // 坐标偏移量数组
int dy[4] = {0,1,0,-1};
while(!q.empty()){
PII f = q.front(); // 取出队头
q.pop(); // 弹出队头元素
for(int i = 0; i < 4; i++){ // 对现在得到的坐标的上下左右四个方向的偏移的各个位置的状况进行查看及操作
int x = f.x + dx[i]; // 各个方向上的坐标
int y = f.y + dy[i];
if(x < 0 || x >= w || y < 0 || y >= h) continue; // 防止越界
if(mapp[x][y] == '#') continue; // 如果是障碍物就退出
if(vis[x][y]) continue; // 遍历过就退出,防止二次统计
vis[x][y] = 1; // 我这时候已经来过这个点了
q.push({x, y}); // 压入队列,后期再看这个点的其他点
}
}
return *; // 可能要返回值
}
题
下面给出四个题:
AC1101
基本上就是上面的简单不扩展应用,这里用了dis数组同时记录距离和访问
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 210;
int T;
char mapp[N][N];
int dis[N][N];
int r, c;
int bfs(PII start, PII end){
memset(dis, -1, sizeof(dis));
queue<PII> q;
dis[start.first][start.second] = 0;
q.push(start);
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
while(!q.empty()){
PII f = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int x = f.first + dx[i];
int y = f.second + dy[i];
if(x < 0 || x >= r || y < 0 || y >= c) continue;
if(mapp[x][y] == '#') continue;
if(dis[x][y] != -1) continue;
dis[x][y] = dis[f.first][f.second] + 1;
if(mapp[x][y] == 'E') return dis[x][y];
q.push({x,y});
}
}
return -1;
}
int main(){
cin >> T;
while(T--){
cin >> r >> c;
for(int i = 0; i < r; i++){
scanf("%s",mapp[i]);
}
PII start, end;
for(int i = 0; i < r; i++){
for(int j = 0; j < c; j++){
if(mapp[i][j] == 'S') start = {i,j};
if(mapp[i][j] == 'E') end = {i, j};
}
}
int d = bfs(start, end);
if(d == -1) cout << "oop!\n";
else cout << d << "\n";
}
return 0;
}
AC1113
这个题也是简单扩展,但是要注意它要求的答案是黑色的瓷砖,只加入黑色的瓷砖的坐标,这样才能求好这个连通块的相关值
也要注意,起始位置就是在黑色瓷砖上的
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
int w, h;
char mapp[30][30];
bool vis[30][30];
int bfs(PII start){
int cnt = 1;
queue<PII> q;
q.push(start);
memset(vis, 0, sizeof(vis));
vis[start.x][start.y] = 1;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
while(!q.empty()){
PII f = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int x = f.x + dx[i];
int y = f.y + dy[i];
if(x < 0 || x >= w || y < 0 || y >= h) continue;
if(mapp[x][y] == '#') continue;
if(vis[x][y]) continue;
if(mapp[x][y] == '.'){
cnt++;
vis[x][y] = 1;
q.push({x, y});
}
}
}
return cnt;
}
int main(){
while(cin >> h >> w && w && h){
for(int i = 0; i < w; i++){
scanf("%s",mapp[i]);
}
PII start;
for(int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
if(mapp[i][j] == '@'){
start = {i, j};
break;
}
// cout << mapp[i][j] << " ";
}
// cout << endl;
}
// cout << start.x << " " << start.y << endl;
cout << bfs(start) << endl;
}
return 0;
}
AC1096
这个题也是简单的扩展;是唯一的连通块的题只不过上升为了三维而已
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
using namespace std;
typedef pair<int, int> PII;
struct node{
int x;
int y;
int z;
};
int dx[6] = {-1,0,1,0,0,0};
int dy[6] = {0,1,0,-1,0,0};
int dz[6] = {0,0,0,0,1,-1};
char mapp[110][110][110];
int l, r, c; // L,R,C 分别表示地牢层数,以及每一层地牢的行数和列数。
int dis[110][110][110];
int bfs(node start, node end){
memset(dis, -1, sizeof(dis));
dis[start.x][start.y][start.z] = 0;
queue<node> q;
q.push(start);
while(!q.empty()){
node f = q.front();
q.pop();
for(int i = 0; i < 6; i++){
int x = f.x + dx[i];
int y = f.y + dy[i];
int z = f.z + dz[i];
if(x < 0 || x >= l || y < 0 || y >= r || z < 0 || z >= c) continue;
if(mapp[x][y][z] == '#') continue;
if(dis[x][y][z] != -1) continue;
dis[x][y][z] = dis[f.x][f.y][f.z] + 1;
if(end.x == x && end.y == y && end.z == z) return dis[x][y][z]; // 到达终点就可以返回距离了
q.push({x,y,z});
}
}
return -1;
}
int work(){
cin >> l >> r >> c;
if(!l && !r && !c) return 0;
for(int i = 0; i < l; i++){
for(int j = 0; j < r; j++){
scanf("%s",mapp[i][j]);
}
}
node start, end;
for(int i = 0; i < l; i++){
for(int j = 0; j < r; j++){
for(int k = 0; k < c; k++){
if(mapp[i][j][k] == 'S') start = {i, j, k};
if(mapp[i][j][k] == 'E') end = {i, j, k};
}
}
}
int d = bfs(start, end);
if(d == -1) cout << "Trapped!" << endl;
else printf("Escaped in %d minute(s).\n", d );
return 1;
}
int main(){
while(work() != 0);
return 0;
}
AC1233
这个题就不一样了,他是有多个连通块;在每个连通块下,需要对每个连通块进行操作
然后找到这个连通块中每个可能将会成海洋的块,然后与在这个连通块中所有曾经是陆地的个数进行对比,如果相等,那么之后就肯定会被完全淹没
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int n;
char mapp[1000 + 10][1000 + 10];
bool vis[1000 + 10][1000 + 10];
int bfs(int xx, int yy){
queue<PII> q;
q.push({xx, yy});
vis[xx][yy] = 1;
int earth = 0;
int sea = 0;
while(!q.empty()){
PII f = q.front()
q.pop();
int t = 0; //看这个点的四个方向的情况!注意,要完全理解flood fill算法中每一步具体是干什么的
earth++;
for(int i = 0; i < 4; i++){
int x = f.x + dx[i];
int y = f.y + dy[i];
if(x < 0 || x >= n || y < 0 || y >= n) continue;
if(vis[x][y] == 1) continue;
if(mapp[x][y] == '.'){
t = 1;
continue;
}
vis[x][y] = 1;
q.push({x, y});
}
if(t) sea ++;
}
return sea == earth;
}
int main(){
cin >> n;
for(int i = 0; i < n; i++){
scanf("%s", mapp[i]);
}
int cnt = 0; //看连通块的数量
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(vis[i][j] == 0 && mapp[i][j] == '#'){
int x = bfs(i, j);
// cout << x << endl;
if(x) cnt++;
}
}
}
// for(int i = 0; i < )
cout << cnt;
return 0;
}