蓝桥杯2023年第十四届省赛真题-岛屿个数|DFS

题目链接:

蓝桥杯2023年第十四届省赛真题-岛屿个数 - C语言网 (dotcpp.com)

参考视频:

 【[蓝桥杯]真题讲解:岛屿个数(BFS遍历图)】 https://www.bilibili.com/video/BV1uc411v7Tw/?share_source=copy_web&vd_source=9332b8fc5ea8d349a54c3989f6189fd3

(视频用了BFS,并且没有扩容,但是讲解很清楚)

参考题解:

两次DFS(染色法+合并)-Dotcpp编程社区

说明

1.为什么要扩容?

因为需要从一个外海(不在环里的海)作为起点,而在边界的海肯定是不在环里的,因为他至少有一个方向没有岛屿(边界之外全是海水)。后面发现不扩容也可以,直接搜索边界那一圈(i=0,j=0..n-1;i=m-1,j=0..n-1;j=0,i=0....m-1;j=n-1,i=0....m-1),如果有外海肯定能找到,因为不能找到,说明边界上全是1,为岛屿,那么地图内的海和岛屿肯定都在环内了,不用再数了。不过这样就不能一次dfs,要搜索还有没有没访问的外海,有的话要从这个外海开始一次dfs。


2.const int M = 52;//为什么数组大小是52?

因为,由题:对于 100% 的评测用例,1 ≤ T ≤ 10,1 ≤ M, N ≤ 50。

至少要52,因为有时右下角被环挡住了,如果用51只有上边和左边多了海,不再加一行一列的话,访问不到右下角不在环里的海水和岛屿  。

如:0 0 0 0 0
      0|1 1 1 1 |
      0|1 1 1 1 |
      0|1 1 1 0 |
      0|1 1 0 1 |
      显然从0,0开始是不能访问到右下角的不在环里的岛屿和海水的。

3.环里的海和岛屿可以看成跟环是一体的,当外面所有的海水都染色之后,从环上出发DFS(四连通),遇到1和0都是一样的,不用再去把0染成1,所以把这部分代码注释了。

4.注意细节:下标的检查,海水DFS时最外那一圈也要走;多组测试数据,每组开始要清空清数,和最外层的标记。

#include<iostream>
//#include<bits/stdc++.h>
#define int long long
using namespace std;
//为什么要扩容?因为需要从一个外海(不在环里的海)作为起点,出发 
const int M = 52;//至少要52,因为有时右下角被环挡住了,如果不再加一行一列的话,访问不到右下角不在环里的海水和岛屿 ,例子见下面dfs的例子 
const int N = 52;
int mp[M][N] = { 0 };
int t;
int m, n;
int dx[8] = { 0,1,1,1,0,-1,-1,-1 };
int dy[8] = { 1,1,0,-1,-1,-1,0,1 };
int ans = 0;
void dfs(int x, int y) {

    if (mp[x][y] == 0)  mp[x][y] = 2;

    for (int k = 0; k < 8; k++) {
        int xx = x + dx[k], yy = y + dy[k];
    //注意这里要把最外层考虑到,因为有时需要走外层的海水才能到达右下角的海水;岛屿染色时不用考虑
    /*比如:
    0 0 0 0 0
      --------
	0|1 1 1 1 |
	0|1 1 1 1 |
	0|1 1 1 0 |
	0|1 1 0 1 |
	  --------
	  显然从0,0开始是不能访问到右下角的不在环里的岛屿和海水的, 
	*/ 
    
        if (xx >= 0 && yy >= 0 && xx <= m+1 && yy <= n+1) {
            if (mp[xx][yy] == 0)
                dfs(xx, yy);
        }
    }
}
void dfsland(int x, int y) {

    if (mp[x][y] == 1||mp[x][y] ==0) {
        mp[x][y] = 2;
    }

    for (int k = 0; k < 8; k = k + 2) {
        int xx = x + dx[k], yy = y + dy[k];
        if (xx >= 1 && yy >= 1 && xx <= m && yy <= n) {
            if (mp[xx][yy] ==1||mp[xx][yy] ==0)
                dfsland(xx, yy);
        }
    }


}



signed main() {
    scanf("%d", &t);
    while (t--) {
        ans = 0;
        cin >> m >> n;

        //注意:每次要把矩阵全部重新清0,因为输入的时候没输入外层
        //(在原有地图的外面多加了一层)的海水,所以上一次的影响还在
        //检查了好几遍才发现是这里遗漏了
        for (int i = 0; i <= m+1; i++) {
            for (int j = 0; j <= n+1; j++) {
                mp[i][j] = 0;
            }
        }


       //整数数组。输入时要规定每次只输入一位 
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%1d", &mp[i][j]);
            }
        }

        //0,0处是新加的外层,一定为海水,从此处开始(八连通)一定可以到达所有不在环里的海水
        //没到达的海水就在环里,没被染色,相当于这部分海水是一个岛屿的一部分
        dfs(0,0);

        //环里的海水染色成岛屿,这样如果存在子岛屿,可以通过四连通走到。即可认为是环和子岛屿是一个岛屿
//        for(int i=1;i<=m;i++){
//                for(int j=1;j<=n;j++){
//                    if(mp[i][j]==0){
//                        mp[i][j]=1;
//                    }
//                }
//            }

        //从一个岛屿出发,四连通走,染色
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(mp[i][j]==1){
                     ans++;
                    dfsland(i,j);
                }
            }
        }

        cout << ans <<endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CGuts350

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值