一个很重要的点:只有边权为1才能应用BFS算法

习题篇:(12条消息) BFS 之Flood Fill 算法(二)_Dream.Luffy的博客-CSDN博客

算法介绍: 

一如往常,我们先看看Flood Fill算法的定义:

Flood fill算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。

基本作用:寻找连通块

适用的题目类型: 需要找出连通块的题目

整体思路:

1.设置循环遍历地图

2.设置BFS函数

3.将当前所遍历点坐标作为参数传入bfs

4.将参数压入队列中

5.一直循环到队列为空

6. 判断当前坐标是否合法(这里是每个题目的不同点)

7.若状态满足要求,则压入队列中,重置状态

只有文字,没有图可不是我们的风格

我们先看看最初始的情况,就是每一个点都是合法的,都能倒水,那么我们可以得到下图

重要的是,我们怎么将它转换为代码表示

这里就需要用到 数据结构 队列来操作了,如下图

我们用队列存储灌了水的格子,再遍历与它接壤的格子,不断更新直到队列为空

那么,我们先来看一个典题

池塘计数(来自于信息学奥赛一本通)

农夫约翰有一片 N∗M 的矩形土地。

最近,由于降雨的原因,部分土地被水淹没了。

现在用一个字符矩阵来表示他的土地。

每个单元格内,如果包含雨水,则用”W”表示,如果不含雨水,则用”.”表示。

现在,约翰想知道他的土地中形成了多少片池塘。

每组相连的积水单元格集合可以看作是一片池塘。

每个单元格视为与其上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。

请你输出共有多少片池塘,即矩阵中共有多少片相连的”W”块。

输入格式

第一行包含两个整数 N 和 M。

接下来 N 行,每行包含 M 个字符,字符为”W”或”.”,用以表示矩形土地的积水状况,字符之间没有空格。

输出格式

输出一个整数,表示池塘数目。

数据范围

1≤N,M≤10001≤N,M≤1000

输入样例:

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

输出样例:

3

 算法分析:

我们先来分析样例,w表示积水,而且是八连通的, 我们可以看到有三处积水 是互不连通

这里,我们就可以采用Flood Fill算法。

首先遍历整个地图,若该点有积水且没被遍历过(一定要是没被遍历过的,否则时间复杂度会倍增),那么我们就将该点传入BFS中,

以该积水块为起点, 遍历接壤的每一个点,执行上面的4, 5, 6, 7阶段,直到队列为空,也就是已经遍历了所有连通的积水块我们返回true, 表示这是一个积水连通块。

代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>#define x first
#define y secondusing namespace std;
typedef pair<int, int> PII; //用pair数组来存点的坐标const int N = 1010, M = N * N;int n, m;
char g[N][N];
PII q[M]; //模拟队列的数组
bool st[N][N]; //st数组用于存储该点是否被遍历过void bfs(int sx, int sy)
{int hh = 0, tt = 0; //模拟队列q[0] = {sx, sy};st[sx][sy] = true;while(hh <= tt){PII t = q[hh ++];for(int i = t.x - 1; i <= t.x + 1; i ++)  //遍历八连通点for(int j = t.y - 1; j <= t.y + 1;j ++){if(i == t.x && j == t.y) continue; //挖去自身中间那个点if(i < 0 || i >= n || j < 0 || j >= m) continue;if(g[i][j] == '.' || st[i][j]) continue; //如果是陆地或已被遍历过q[ ++ tt ] = {i, j}; //将该接壤点入队st[i][j] = true;}}
}
int main()
{scanf("%d%d", &n, & m);for(int i = 0;i < n;i ++) scanf("%s", g[i]);int cnt = 0;for(int i = 0;i < n;i ++)for(int j = 0;j < m;j ++)if(g[i][j] == 'W' && !st[i][j]) //如果是水地且没有被遍历过{bfs(i ,j);cnt ++; //连通的积水块区域 +1}printf("%d\n", cnt);
}

看了代码,你可能有两个点不明白:
一. 为什么要用数组模拟队列,如何用数组模拟队列

二.    for(int i = t.x - 1; i <= t.x + 1; i ++)  //遍历八连通点
            for(int j = t.y - 1; j <= t.y + 1;j ++)

这两个循环是什么意思?

问题一:

相比于容器中的队列,用数组模拟速度更快,而且也轻巧方便

怎么用数组模拟队列呢?

我们只需让队头 hh = 0, 队尾 tt = 0,

当我们想加入一个点到队头, 就让q[0] = {sx, sy};

读取队头并把队头抛弃: PII t =  q[hh++]

将点加入到队尾中 q[++ tt] = {i , j};

问题二:
这里借用一下我们上一章状态压缩DP的图哈哈

{tx, ty} 是红色点的坐标, 而我们目标是遍历所有与它接壤的点,并判断它是否合法。

,那么我们左上角的坐标 就对应{tx - 1, ty - 1},  右下角坐标 就对应{tx + 1, ty + 1};

其它点同理。

这样,就解决咯!

java代码:

import java.io.*;
public class Main{static int N = 1010,n,m,tt,hh;static PII[] q = new PII[N * N];static char[][] g = new char[N][N];static boolean[][] st = new boolean[N][N];public static void bfs(int x,int y){hh = 0;tt = -1;q[++ tt] = new PII(x,y);while(hh <= tt){PII t = q[hh ++ ];int a = t.x;int b = t.y;//模拟可以走的方向上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。for(int i = a - 1 ; i <= a + 1 ; i ++ ){for(int j = b - 1 ; j <= b + 1 ; j ++ ){if(i == a && j == b) continue; // 跳过自己那个点if(i < 0 || j < 0 || i >= n || j >= m) continue; //判断是否越界if(g[i][j] == '.' || st[i][j]) continue; // 不是水 并且 它已经被标记过了q[++ tt] = new PII(i,j);st[i][j] = true;}}}}public static void main(String[] args)throws IOException{BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));String[] s = bf.readLine().split(" ");n = Integer.parseInt(s[0]);m = Integer.parseInt(s[1]);for (int i = 0 ; i < n ; i ++ ){String str = bf.readLine();char[] c = str.toCharArray();for (int j = 0 ; j < m ; j ++ ){g[i][j] = c[j];}}int cnt = 0;for (int i = 0 ; i < n ; i ++ ){for (int j = 0 ; j < m ; j ++ ){//如果是水且没有被标记过就进行bfsif(g[i][j] == 'W' && !st[i][j]){bfs(i,j);cnt ++;}}    }System.out.println(cnt);}
}
class PII{int x,y;public PII(int x,int y){this.x = x;this.y = y;}
}

python代码:

import sys
line = sys.stdin.readline().strip()
n,m = [int(t) for t in line.split(' ')]
nums = []
for i in range(n):line = sys.stdin.readline().strip()nums.append(line)st = [[False for j in range(m)] for i in range(n)]def bfs(sx, sy):'''sx: start xsy: start y'''# 数组模拟队列, hh-队头, tt-队尾# 一开始队列只有一个元素为起点hh = 0; tt = 0q = [(sx,sy)]st[sx][sy] = True# 当队列不空时, 执行以下程序while (hh <= tt):# 先把队头取出来cur = q[hh]; hh += 1# 遍历当前点周围的8个点for i in range(cur[0]-1, cur[0]+2):for j in range(cur[1]-1, cur[1]+2):if i == cur[0] and j == cur[1]: continueif (i<0) or (i>=n) or (j<0) or (j>=m): continueif (nums[i][j] != 'W') or (st[i][j]): continue# 将合法元素加入到队列中q.append((i,j)); tt += 1st[i][j] = Truecnt = 0
for i in range(n):for j in range(m):if (nums[i][j] == 'W') and (not st[i][j]):bfs(i, j)cnt += 1
print(cnt)

贴上状态压缩DP讲解,也很详细哦!状态压缩DP 图文详解(一)_Dream.Luffy的博客-CSDN博客

以及习题篇(12条消息) BFS 之Flood Fill 算法(二)_Dream.Luffy的博客-CSDN博客

该系列会持续更新, 我是Luffy,期待与你再次相遇

BFS 之Flood Fill 算法相关推荐

  1. 算法提高课-搜索-Flood fill算法-AcWing 1097. 池塘计数:flood fill、bfs

    Flood fill 算法简介: 像洪水一样,一圈一圈往外蔓延,像bfs. flood fill 算法可以在线性复杂度内,找到某个点所在的连通块. 题目分析 来源:acwing ac代码 #inclu ...

  2. 算法提高课-搜索-Flood fill算法-AcWing 1106. 山峰和山谷:flood fill、bfs

    题目分析 来源:acwing 分析:这道题还是flood fill算法的应用,不同点在于八个方向扫描,习惯性采用二重循环来扫描周围的8个方向:其次,这里需要统计周围比它高的和比它矮的,这点用bool变 ...

  3. 算法提高课-搜索-Flood fill算法-AcWing 1098. 城堡问题:flood fill、bfs

    题目分析 来源:acwing 分析:找房间个数,也就是找连通的个数. 样例画出来的房间个数如下图:其中'|' 和'-'不是墙,只有#是墙. 分析:这题不用建图,直接bfs(flood fill)来做, ...

  4. 算法提高课学习——2.搜索——2.1.Flood Fill算法

    Flood Fill Flood Fill算法用于求图中的连通块数量,一般用BFS实现,不过也可以使用DFS来实现,更加简洁,但同时也可能会有爆栈的风险 对于图中的任意一个点,我们从该点出发标记它所有 ...

  5. 常见算法之Flood Fill算法

    常见算法之Flood Fill算法 算法介绍 基本作用:寻找连通块 基本方法:BFS搜索 适用题目:需要找出分类块的题目/一些聚类问题 顾名思义,Flood Fill算法就是像洪水泛滥一样去寻找周围符 ...

  6. 红与黑 ( Flood fill 算法 (针对网格题) )

    题目描述 有一间长方形的房子,地上铺了红色.黑色两种颜色的正方形瓷砖. 你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动. 请写一个程序,计算你总共能够到达多少块黑色的瓷砖. ...

  7. Flood Fill算法总结

    最近一直跟着y总在刷蓝桥杯的课,借此我来对里面涉及到的Flood Fill算法做一个我目前接触到的所有的flood fill算法的总结 总 首先,这本质上是一个搜索算法.他的作用是对一个图(目前我用到 ...

  8. flood fill算法

    flood fill算法实现有三种形式 1.depth first search 2.breadth first search 3.breadth first scan 基本思想是找到还没有分配com ...

  9. 洪水填充算法_洪水填充(Flood fill)算法

    洪水填充(Flood fill)算法 从一个起始节点开始把附近与其连通的节点提取出或填充成不同颜色颜色,直到封闭区域内的所有节点都被处理过为止,是从一个区域中提取若干个连通的点与其他相邻区域区分开(或 ...

最新文章

  1. C++成员变量和成员函数分开存储
  2. QT子线程与主线程的信号槽通信
  3. 微信自动回复如何实现?用 Python 就可以!
  4. vue子组件向父组件传递数据
  5. 打印Fibonacci数列方法汇总(前20项,每行5个)
  6. React 生命周期
  7. UBUNTU18禁用锁屏功能
  8. wps的流程图怎么导出_WPS如何绘制流程图? WPS绘制流程图的详细教程
  9. 模型预测控制的缺点_模型预测控制(MPC)与最优控制的理论分析 | NeurIPS 2020
  10. 在信息流上,百度或许可以给谷歌信息流支个招
  11. CentOS8 启动错误,enter emergency mode 报错 Failed to mount /sysroot 解决方法
  12. PC端浏览器兼容测试总结
  13. Linux 中 TCL 和 Expect语法
  14. 纯字体logo怎么设计?设计logo几步完成
  15. 激光雷达基础:坐标系
  16. 干货 | 软件工程师必知之事 —— 如何定义自己的职业路线?
  17. 基于神经网络算法LSTM模型对股票指数进行预测
  18. java catch中抛出异常_简单了解Java编程中抛出异常的方法
  19. PDF转图片哪个软件好?推荐这几个软件
  20. 一个馒头引发的血案...请看完无极后观看此片,保笑死人不偿命

热门文章

  1. 华为boss力荐公司高层看的一篇文章,真的很经典!!![转载]
  2. sqlserver查看执行计划
  3. java毕设 springboot公寓宿舍后勤管理系统(含源码+论文)
  4. 新版盲盒交友程序源码V3
  5. android反编译和防止反编译的方法和原理
  6. 天啊,MLDN中心真让我懂得什么叫学习了.坚持,为了高薪
  7. silverlight ajax调用,使用异步AJAX时,Silverlight应用程序不会变为空闲
  8. 51单片机学习:LCD1602液晶显示实验
  9. C语言uthash使用指南
  10. 产业互联网方法论:腾讯的C to B蹊径