• BFS 之Flood Fill 算法(二)


    在上一篇文章BFS 之Flood Fill 算法_Dream.Luffy的博客-CSDN博客中,

     

    我们了解到Flood Fill算法的用途,以及如何使用,并给出了例题讲解,而本文是针对Flood Fill算法的习题练习篇。

    例一:

    城堡问题(来自于信息学奥赛一本通)

    1. 1 2 3 4 5 6 7
    2. #############################
    3. 1 # | # | # | | #
    4. #####---#####---#---#####---#
    5. 2 # # | # # # # #
    6. #---#####---#####---#####---#
    7. 3 # | | # # # # #
    8. #---#########---#####---#---#
    9. 4 # # | | | | # #
    10. #############################
    11. (图 1)
    12. # = Wall
    13. | = No wall
    14. - = No wall
    15. 方向:上北下南左西右东。

    图1是一个城堡的地形图。

    请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。

    城堡被分割成 m∗n个方格区域,每个方格区域可以有0~4面墙。

    注意:墙体厚度忽略不计。

    输入格式

    第一行包含两个整数 m 和 n,分别表示城堡南北方向的长度和东西方向的长度。

    接下来 m 行,每行包含 n 个整数,每个整数都表示平面图对应位置的方块的墙的特征。

    每个方块中墙的特征由数字 P 来描述,我们用1表示西墙,2表示北墙,4表示东墙,8表示南墙,P 为该方块包含墙的数字之和。

    例如,如果一个方块的 P 为3,则 3 = 1 + 2,该方块包含西墙和北墙。

    城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。

    输入的数据保证城堡至少有两个房间。

    输出格式

    共两行,第一行输出房间总数,第二行输出最大房间的面积(方块数)。

    数据范围

    1≤m,n≤50,
    0≤P≤15

    输入样例:

    1. 4 7
    2. 11 6 11 6 3 10 6
    3. 7 9 6 13 5 15 5
    4. 1 10 12 7 13 7 5
    5. 13 11 10 8 10 12 13

    输出样例:

    1. 5
    2. 9

     算法分析:

    首先,由题意可以得出该题考查的是连通性问题,故我们可以想到用Flood Fill算法来解决

    本题是一个四连通问题(上下左右)

    思考本题与上题的不同点

    ①:地图的读入方式不同

    ②:需要输出的结果不同

    针对问题一:由于1表示西墙,2表示北墙,4表示东墙,8表示南墙,我们可以利用二进制数来表示一个格子在哪些地方有墙,哪些地方没墙

    例如11的二进制表示: 1011  , 15的二进制表示: 1111 ,即四面都有墙   故我们可以利用二进制1的位置来判断每一面墙的位置(二进制的用法在这篇文章中有讲:状态压缩DP 图文详解(一)_Dream.Luffy的博客-CSDN博客

    针对问题二:由于本题要求最大的房间数,我们可以维护一个变量,每次让BFS返回每个房间的大小,更新这个变量即可

    上代码:

    1. //利用二进制判断是否存在墙
    2. //在t.x, t.y 加上偏移量后 利用二进制进行判断对应位置是否存在墙,即是否能通过
    3. #include
    4. #include
    5. #include
    6. #define x first
    7. #define y second
    8. using namespace std;
    9. typedef pair<int,int> PII;
    10. const int N = 55, M = N * N;
    11. int n, m;
    12. int g[N][N];
    13. PII q[M];
    14. bool st[N][N];
    15. int bfs(int sx, int sy)
    16. {
    17. int dx[4] = {0 , -1, 0, 1}, dy[4] = {-1, 0, 1, 0}; //定义左右上下偏移量
    18. int hh = 0, tt = 0;
    19. int area = 0;
    20. q[0] = {sx, sy};
    21. st[sx][sy] = true;
    22. while(hh <= tt)
    23. {
    24. PII t = q[hh ++];
    25. area ++; //房间面积++
    26. for(int i = 0;i < 4;i ++)
    27. {
    28. int a = t.x + dx[i], b = t.y + dy[i];
    29. if(a < 0 || a >= n || b < 0 || b >= m) continue;
    30. if(st[a][b]) continue;
    31. if(g[t.x][t.y] >> i & 1) continue; //(t.x, t.y) 和(a, b)之间有墙则不连通
    32. q[++ tt] = {a, b};
    33. st[a][b] = true;
    34. }
    35. }
    36. return area;
    37. }
    38. int main()
    39. {
    40. cin >>n >> m;
    41. for(int i = 0;i < n;i ++)
    42. for(int j = 0;j < m;j ++)
    43. cin >> g[i][j];
    44. int cnt = 0, area = 0;
    45. for(int i = 0;i < n;i ++ )
    46. for(int j = 0;j < m;j ++ )
    47. if(!st[i][j])
    48. {
    49. area = max(area, bfs(i, j));
    50. cnt ++;
    51. }
    52. cout << cnt << endl;
    53. cout << area << endl;
    54. }

    这里涉及一个位运算, >> 表示右移(即除以二), 例如二进制数1001 >> 1  得到100, 11 >> 1 = 1;

    位运算&:

    只有两个数的同一位二进制都为1时才为1, 例如 100 & 101  = 101  , 111 & 001 = 1;

    所以我们常用1 与一个数字相& ,来判断这个数字的最后一个二进制数是否为1。

     例题二:

     山峰和山谷(来自于信息学奥赛一本通)

    FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。

    为了能够对旅程有一个安排,他想知道山峰和山谷的数量。

    给定一个地图,为FGD想要旅行的区域,地图被分为 n×n 的网格,每个格子 (i,j) 的高度 w(i,j) 是给定的。

    若两个格子有公共顶点,那么它们就是相邻的格子,如与 (i,j) 相邻的格子有(i−1,j−1),(i−1,j),(i−1,j+1),(i,j−1),(i,j+1),(i+1,j−1),(i+1,j),(i+1,j+1)。

    我们定义一个格子的集合 S 为山峰(山谷)当且仅当:

    1. S 的所有格子都有相同的高度。
    2. S 的所有格子都连通。
    3. 对于 s 属于 S,与 s 相邻的 s′ 不属于 S,都有 ws>ws′山峰),或者 ws
    4. 如果周围不存在相邻区域,则同时将其视为山峰和山谷。

    你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。

    输入格式

    第一行包含一个正整数 n,表示地图的大小。

    接下来一个 n×n 的矩阵,表示地图上每个格子的高度 w。

    输出格式

    共一行,包含两个整数,表示山峰和山谷的数量。

    数据范围

    1≤n≤1000,
    0≤w≤10^9

    输入样例1:

    1. 5
    2. 8 8 8 7 7
    3. 7 7 8 8 7
    4. 7 7 7 7 7
    5. 7 8 8 7 8
    6. 7 8 8 8 8

    输出样例1:

    2 1
    

    输入样例2:

    1. 5
    2. 5 7 8 3 1
    3. 5 5 7 6 6
    4. 6 6 6 2 8
    5. 5 7 2 5 8
    6. 7 1 0 1 7

    输出样例2:

    3 3
    

    样例解释

    样例1:

    样例2:

    算法分析: 

    由题意知,本题是一个八连通问题, 八连通问题在上一文中讲过,这里就不再赘述

    我们可以发现山峰,即山脉周围没有比它高的边界。

    山谷即 山脉周围。

    所以我们只需要在BFS过程中判断,  是否存在比山脉高或低的边界。

    代码如下:

    1. #include
    2. #include
    3. #include
    4. #define x first
    5. #define y second
    6. using namespace std;
    7. typedef pair<int,int> PII;
    8. const int N = 1010;
    9. PII q[N * N];
    10. int g[N][N];
    11. bool st[N][N];
    12. int n;
    13. void bfs(int sx,int sy, bool &has_higher, bool &has_lower)
    14. {
    15. int hh = 0, tt = 0;
    16. q[0] = {sx, sy};
    17. st[sx][sy] = true;
    18. while(hh <= tt)
    19. {
    20. PII t = q[hh ++ ];
    21. for(int i = t.x - 1; i <= t.x + 1;i ++)
    22. for(int j = t.y - 1;j <= t.y + 1;j ++) //八连通的遍历方式
    23. {
    24. if(i == t.x && j == t.y) continue; //除去自己
    25. if(i < 0 || i >= n || j < 0 || j >= n) continue;
    26. if(g[t.x][t.y] != g[i][j]) //山脉的边界如果不等于山
    27. {
    28. if(g[i][j] > g[t.x][t.y]) has_higher = true;
    29. else has_lower = true;
    30. }
    31. else if(!st[i][j]) //如果一样高,则加入队列继续遍历
    32. {
    33. q[++ tt] = {i, j};
    34. st[i][j] = true;
    35. }
    36. }
    37. }
    38. }
    39. int main()
    40. {
    41. cin >> n;
    42. for(int i = 0;i < n;i ++)
    43. for(int j = 0;j < n;j ++)
    44. cin >> g[i][j];
    45. int peak = 0, valley = 0;
    46. for(int i = 0;i < n;i ++)
    47. for(int j = 0;j < n;j ++)
    48. if(!st[i][j])
    49. {
    50. bool has_higher = false, has_lower = false;
    51. bfs(i, j, has_higher, has_lower);
    52. if(!has_higher) peak ++; //边界没有比山脉高的则为山峰
    53. if(!has_lower) valley ++ ; //同理
    54. }
    55. cout << peak << " " << valley;
    56. return 0;
    57. }

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

     

  • 相关阅读:
    ML class Note——回归
    数据结构习题(顺序表)
    vue父组件如何向子组件传递数据?
    mysql MVCC(多版本并发控制)理解
    学习自旋电子学的笔记07:根据微磁学基本能量密度公式推导有效场
    Canal + Kafka 同步 MySQL 数据到 Redis
    MySQL 主从复制、读写分离
    Pytorch面试题面经
    Unity脚本的基础语法(8)-协同程序与销毁方法
    MySQL 锁的内存结构
  • 原文地址:https://blog.csdn.net/m0_64226820/article/details/126328256