AcWing 1097 池塘计数

题目描述:

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

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

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

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

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

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤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

分析:

终于基本刷完了DP章节的题目,可以进入和蔼可亲的搜索章节了。本题是Flood Fill的典型例题,顾名思义,洪水覆盖,地面上有高地和洼地,洪水会逐渐扩散直至覆盖所有的洼地。Flood Fill问题可以用DFS或者BFS解决,一般还是采用BFS,可以防止爆栈,毕竟函数内部的栈空间只有1M大小。

本题比较简单,就简单的说下基本的解题思路吧。首先,题意是求八连通块的数目,八连通块的遍历和四连通块一样,都可以使用方向向量dx和dy,遍历特定的坐标而不用使用二重循环遍历矩形的区域。之前实现BFS习惯直接使用queue,现在习惯了直接用数组模拟队列的便捷和快速,就直接用数组模拟的队列实现了。基本思路是,将当前坐标入队,同时置访问位为true,当队列非空时,取出队头元素,尝试遍历队头元素周围的八连通区域,当到达每个位置的坐标合法时,就将该位置的坐标也入队。判断某个位置是否合法,只需要判断该位置是否已经访问过,是否超过了单元格的边界,以及是否不含雨水不能通行。

这里是在每个元素入队时就将访问位置为true,实际上记录的是discovered时间,如果遍历完该元素周围所有的元素再置为true,记录遍历元素的visited时间也是可以的,简易版的BFS程序不论是入队时置为已访问,还是出队时或者遍历完时都无关紧要。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
typedef pair<int,int> PII;
char g[N][N];
bool st[N][N];
PII q[N * N];
int n,m;
int dx[] = {0,0,-1,1,-1,-1,1,1};
int dy[] = {1,-1,0,0,-1,1,-1,1};
void bfs(int x,int y){
    int hh = 0,tt = 0;
    q[0] = {x,y};
    st[x][y] = true;
    while(hh <= tt){
        PII t = q[hh++];
        for(int i = 0;i < 8;i++){
            int nx = t.first + dx[i],ny = t.second + dy[i];
            if(st[nx][ny]||g[nx][ny]=='.'||nx<0||nx>=n||ny<0||ny>=m)    continue;
            q[++tt] = {nx,ny},st[nx][ny] = true;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 0;i < n;i++)   scanf("%s",&g[i]);
    int res = 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);
                res++;
            }
        }
    }
    printf("%d\n",res);
    return 0;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值