• POJ 2386 Lake Counting


    传送门

    题面:

    Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100) squares. Each square contains either water ('W') or dry land ('.'). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors.

    Given a diagram of Farmer John's field, determine how many ponds he has.

    Input

    • Line 1: Two space-separated integers: N and M

    • Lines 2..N+1: M characters per line representing one row of Farmer John's field. Each character is either 'W' or '.'. The characters do not have spaces between them.

    Output

    • Line 1: The number of ponds in Farmer John's field.

    Sample Input
    10 12
    W........WW.
    .WWW.....WWW
    ....WW...WW.
    .........WW.
    .........W..
    ..W......W..
    .W.W.....WW.
    W.W.W.....W.
    .W.W......W.
    ..W.......W.(样例建议看原题)

    Sample Output
    3

    Hint

    OUTPUT DETAILS:

    There are three ponds: one in the upper left, one in the lower left,and one along the right side.

    题面描述

    农场下雨了,然后想知道农场有多少个水坑,这里用'W'表示这块地有水,判断是否为同一个水坑的标志是水连在一起,也就是:如果一个水的相邻方向(上下左右和对角)都有水的话,那么它们就属于同一个水坑。具体可以看样例。

    题目分析

    呃,这是一道经典的用dfs求连通块的题。什么是连通块?这里的水坑就是一个连通块。那么,我们如何用dfs,即递归的方法完成这道题呢?
    我们先看看AC代码:

    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn = 100+5;
    char a[maxn][maxn];
    int n, m;
    
    bool check(int r, int c){  //越界检查
        if(r < 0 || r >= n || c < 0 || c >= m) return false;
        return true;
    }
    
    void dfs(int r, int c){
        if(a[r][c] == '#') return;  //被标记过就不用继续标记了
        a[r][c] = '#';  //标记
        int r1, c1;
        for(r1 = -1; r1 <= 1; r1++)    //遍历上下左右对角(九宫格),这里不会遍历到自己
            for(c1 = -1; c1 <= 1; c1++)
                if(check(r+r1, c+c1) && a[r+r1][c+c1] == 'W')  
                    dfs(r+r1, c+c1);   //找到相邻的'W'继续进行遍历
        //遍历完相邻的'W'后回溯,这里return省略不写
    }
    
    int main(){
        memset(a, 0, sizeof(a));  //清零
        cin >> n >> m;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                cin >> a[i][j];
        int cnt = 0;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
               if(a[i][j] == 'W') {dfs(i, j); cnt++;} //找到一个水而且没被标记过,就进行深度搜索,计数
        cout << cnt << endl;
        return 0;
    }
    

    这里涉及到一个新的方法:种子填充,我先摆在这里,后面再讲。下面是维基百科演示种子填充的gif图:


    我们一步步分析代码怎么得来,先看主函数做了什么:

        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
               if(a[i][j] == 'W') {dfs(i, j); cnt++;}
    

    这里的思路就是,当我找到一个水后,把相邻的水连通起来,然后计数:

    在这里插入图片描述

    那么,我们“连通水”这个函数怎么写呢?这里就要用到我们之前说的方法,种子填充。其实通俗点理解就是做标记。就像这样:
    在这里插入图片描述

    为什么要做标记呢?如果不做标记,主函数就会继续遍历到同一个水坑。所以,我们的函数就实现两个功能:一:做标记,二:遍历相邻的水然后连通它们。而且我们也注意到,当遍历到其中一个相邻的水时,这个相邻的水又可以做为中心点去遍历其他的水,从而达到连通的目的。这提示了我们可以用递归来解决这个重复做的事,所以大概代码是这样:

    void dfs(int r, int c){
        a[r][c] = '#';   //标记自己本身
        int r1, c1;
        for(r1 = -1; r1 <= 1; r1++)   //遍历相邻的水
            for(c1 = -1; c1 <= 1; c1++)
                if(a[r+r1][c+c1] == 'W')
                    dfs(r+r1, c+c1);
        //遍历完后自动回溯    
    }
    

    到了这里,好像还少了些什么?没错,可能会出现数组越界访问的问题,所以只需要这样:

    void dfs(int r, int c){
        a[r][c] = '#'; 
        int r1, c1;
        for(r1 = -1; r1 <= 1; r1++)   
            for(c1 = -1; c1 <= 1; c1++)
                if(r+r1 >= 0 && r+r1 < n && c+c1 >= 0 && c+c1 < m && a[r+r1][c+c1] == 'W')
                    dfs(r+r1, c+c1);
    }
    

    因为我忍受不了一行这么长的代码,所以 我用了一个check()函数解决这个问题。
    然后这道题就解决了。

    有人会问,我的dfs函数的代码还多了个if(a[r][c] == '#') return;,这个目的是防止重复标记,不过对于数据比较少的题目这个没有多大的影响,可有可无。

  • 相关阅读:
    程序笔记
    2011年11月28日学习重构
    经典到发狂的语录(某男日记摘录)
    每天养成好习惯
    jquery用法
    [读书笔记]软件架构师应该知道的97件事
    Entity Framework 4.2发布,部分更新等待.NET Framework 4.5
    我的阅书记录及相关专业书籍推荐(更新于2017.12)
    [2011 年终项目总结] 第四章、架构设计
    [2011 年终项目总结] 第五章、迭代开发
  • 原文地址:https://www.cnblogs.com/happy-MEdge/p/10544533.html
Copyright © 2020-2023  润新知