Flood Fill算法总结

最近一直跟着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. 献给阿尔吉侬的花束

AC1113. 红与黑

AC1096. 地牢大师

AC1233. 全球变暖


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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值