题目链接:
蓝桥杯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;
}