POJ2386 Lake Counting & 2018年蓝桥杯I题全球变暖

POJ2386 Lake Counting 和 2018年蓝桥杯I题 全球变暖 都属于深搜的同一种类型的题,都与计数有关,比较有共同点和举一反三的感觉所以放在一起讨论一下。

POJ2386 Lake Counting

题目大意:
给出N x M大小的空间,每一点可能是空地可能是湖,其中湖用’W’表示,空地用’.'表示,如果一个点是湖,如果该点周围的八个点中也存在湖那么这两点就构成同一片湖。求这片空间一共有几个湖。

思路:
循环遍历整个空间的每一个点,如果这个点没有被搜过并且是湖的话就进行深搜,此时相当于找到了一个新的湖,计数加一。因为每一次进行深搜都会把附近所有的湖都搜一遍,这个过程中搜到的所有湖都属于同一片湖并且都进行了标记,在接下来的遍历中就不会再访问已经访问过的点了。

完整代码如下:

#include<stdio.h>
int book[101][101] ;
char map[101][101];
int next[8][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}, {-1,-1}, {-1,1}, {1,-1}, {1,1}} ;
int n, m ;
void dfs(int x, int y) {
    int tx, ty ;
    for(int i=0; i<8; i++) {            //遍历该点周围的八个方向
        tx = x + next[i][0] ;
        ty = y + next[i][1] ;
        if(tx<0 || tx>=n || ty<0 || ty>=m)      //防止越界
            continue ;
        if(book[tx][ty]==0 && map[tx][ty]=='W') {
            book[tx][ty] = 1 ;          //将同一个湖泊里的水都标记即可
            dfs(tx, ty) ;
        }
    }
    return ;
}
int main() {
    int sum = 0 ;
    scanf("%d%d", &n, &m) ;
    for(int i=0; i<n; i++)
        scanf("%s", map[i]) ;
    for(int i=0; i<n; i++) {        //遍历整个空间
        for(int j=0; j<m; j++) {
            if(book[i][j]==0 && map[i][j]=='W') {
                sum++ ;         //每找到一个没有标记过的水则是一个新的湖泊
                book[i][j] = 1 ;
                dfs(i, j ) ;    //将该湖泊里的所有水都标记
            }
        }
    }
    printf("%d\n", sum) ;
    return 0 ;
}

2018年蓝桥杯I题全球变暖

这道题就是在上一道题的基础上有所改变,有一种举一反三的感觉。

题目大意:
给出N x N大小的空间,每一点可能是空地可能是海,其中海用’.‘表示,空地用’#'表示,如果一个点是空地,如果该点周围的四个点中也存在空地那么这两点就构成同一片岛屿。现在如果一个空地的四周任意一个方向存在海,那么这个空地就会被淹没成海。求这片空间中的空地被海淹没之后还剩下几个岛屿。

思路:
这个题的思路和上一题类似,通过循环遍历整个空间的每个点进行计数。如果这个点没有被搜过并且是空地的话就进行深搜,此时相当于找到了一个新的岛屿,因为每一次进行深搜都会把附近所有的空地都搜一遍,这个过程中搜到的所有空地都属于同一个岛屿并且都进行了标记,在接下来的遍历中就不会再访问已经访问过的点了。在这个过程中同样要在每一次进行深搜之后进行计数,但是现在的计数是有条件的:这个岛屿没有被完全淹没才计数。在深搜的过程中可以先给搜过的空地的标记book[x][y]赋值,如果该点周围存在海那就改变book的值,如果四周都不是海那么该点就不会被淹没,这个岛屿就可以被计数。

完整代码如下:

#include<stdio.h>
int n, sum = 0 ;
char map[1005][1005] ;
int book[1005][1005] ;
int next[4][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}} ;
bool flag ;         //标记是否存在没有被淹没的地方
void dfs(int x, int y) {
    int tx, ty ;
    for(int i=0; i<4; i++) {    //遍历一个点的四个方向
        tx = x + next[i][0] ;
        ty = y + next[i][1] ;
        if(tx<0 || ty<0 || ty>=n ||tx>=n)       //防止越界
            continue ;
        if(map[tx][ty] == '.') {        //如果这个点的四周存在水这个点就会被淹没
            book[x][y]-- ;
            continue ;
        }
        if(map[tx][ty]=='#' && book[tx][ty]==0) {   //如果隔壁点是陆地并且没搜过就搜
            book[tx][ty] = 5 ;          //先标记搜过
            dfs(tx, ty) ;
        }
    }
    if(book[x][y] == 5)     //如果一个点遍历完四周都没有水则该岛存在没有被淹没的地方
        flag = true ;
}
int main() {
    scanf("%d", &n) ;
    for(int i=0; i<n; i++)
            scanf("%s", &map[i]) ;
    for(int i=0; i<n; i++) {        //遍历整个地图
        for(int j=0; j<n; j++) {
            if(book[i][j]==0 && map[i][j]=='#') {   //如果该点没有搜过并且是陆地,那么这是一个新的岛
                flag = false ;
                dfs(i, j) ;
                if(flag)
                    sum++ ;
            }
        }
    }
    printf("%d\n", sum) ;
}

这道题我认为有一个容易出错的地方:一个空地被淹没后是否会继续“腐蚀”岛屿更深的地方。按理来说如果会“腐蚀”的话那就肯定不存在任何一个岛屿,所以我认为在深搜过程中如果找到一个空地周围有海会被淹没就把这个空地的值改为"."是不对,可能会对之后搜索的点造成影响。
这只是我在其他人的博客里看到有这样的作法但没有细看代码,所以只是有这样的猜测。可能是我漏看了什么关键的语句吧。

总结:

  • 把类似的题总结在一起来看是很有意思的事,举一反三,修改一点题目就可以在最基础的方法上创造出新的东西,这样学习很有效吧,印象比较深刻。
  • 写完一道题后再比较其他人写的代码,总是能有所收获,看到自己好的地方也会感叹别人的一些很巧妙的写法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章