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) ;
}

這道題我認爲有一個容易出錯的地方:一個空地被淹沒後是否會繼續“腐蝕”島嶼更深的地方。按理來說如果會“腐蝕”的話那就肯定不存在任何一個島嶼,所以我認爲在深搜過程中如果找到一個空地周圍有海會被淹沒就把這個空地的值改爲"."是不對,可能會對之後搜索的點造成影響。
這只是我在其他人的博客裏看到有這樣的作法但沒有細看代碼,所以只是有這樣的猜測。可能是我漏看了什麼關鍵的語句吧。

總結:

  • 把類似的題總結在一起來看是很有意思的事,舉一反三,修改一點題目就可以在最基礎的方法上創造出新的東西,這樣學習很有效吧,印象比較深刻。
  • 寫完一道題後再比較其他人寫的代碼,總是能有所收穫,看到自己好的地方也會感嘆別人的一些很巧妙的寫法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章