- 第 164 篇 -
Date: 2026 - 02- 09 (周一)
Author: 郑龙浩(仟墨)
2026-02-09 周一 | 算法打卡day4
文章目录
-
- [2026-02-09 周一 | 算法打卡day4](#2026-02-09 周一 | 算法打卡day4)
5_卡码网101_孤岛的总面积
使用DFS或者BFS都可以做出这类题目
题目描述
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被陆地单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。
现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0。
输出描述
输出一个整数,表示所有孤岛的总面积,如果不存在孤岛,则输出 0。
输入示例
4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
输出示例
1
我的代码
我使用了3中做法,DFS两个,BFS一个
cpp
// Date: 2026-02-09 Author:郑龙浩
// 5_卡码网101_孤岛的总面积
// 题目要求计算"孤岛"的总面积
// 方法1:DFS 方法2: BFS
// 思路:
// 孤岛定义:位于矩阵内部且不与边缘接触的陆地组成的岛屿
// 解题步骤:
// 1. 首先,处理所有与边缘相连的陆地:
// - 遍历矩阵的四条边(上、下、左、右)
// - 对边界上的每个陆地(值为1的点),启动DFS/BFS
// - 将这些与边缘相连的陆地全部标记为已访问,但不计入总面积
// - 也可以选择将这些陆地直接"沉没"为海洋(赋值为0)
//
// 2. 然后,计算剩余陆地的总面积:
// - 再次遍历整个矩阵(现在只考虑内部陆地)
// - 对每个未访问的陆地,启动DFS/BFS计算其面积
// - 累加所有内部岛屿的面积
//
// 3. 输出总面积
//
// 关键点:
// - 需要两次遍历:第一次处理边缘连接,第二次计算内部面积
// - 或者可以使用标记法,不修改原矩阵,用visited数组记录
// 用时:
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int S = 0;
// DFS作用:深搜陆地,且将搜索到的陆地变为海洋
// 我理解的显性与隐性DFS的最基本的区别:(有助于我理解)
// 显性:确定传给dfs函数的坐标是合格的,进入后无需判断(x, y)是否合格
// 隐性:不确定传给dfs函数的坐标是合格的,进入后需判断(x, y)是否合格
// 显性:让dfs函数下一个搜索的坐标(nextX, nextY)周围的陆地时,不判断其坐标是否合格,而是让其进入下一层递归再判断(也就是函数开头判断)
// 隐性:只有合规的坐标(nextX, nextY),才会让dfs函数搜索周围的陆地,而这样就可以保证传给dfs的坐标,一定是个合规的陆地,也就无需对传入dfs的坐标(x, y)进行判断是否合规了
// 法1.1 隐性的递归终止条件
void dfs1(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
grid[x][y] = 0;
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
// 只要下一个搜索的是 非越界的 && 陆地 就继续以(nextX, nextY) 为中心搜索陆地
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
// grid[nextX][nextY] = 0;
dfs1(grid, nextX, nextY);
}
}
}
// 法1.2 显性递归终止条件(其实就相当于把循环中的条件提到了函数开头)
void dfs2(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
// 如果(x, y) 越界 && 海洋,就return
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0) {
return;
}
grid[x][y] = 0; // 只有(x, y) 没越界 && 是陆地,才转海洋
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
dfs2(grid, nextX, nextY);
}
}
// 法2
void bfs(vector <vector <int>>& grid, int x, int y) {
if (grid[x][y] == 0) { // 如果传过来的参数是海洋,则直接return,不搜索四周的坐标
return;
} else {
grid[x][y] = 0;
}
int height = grid.size();
int width = grid[0].size();
queue <pair <int, int>> que; // 存储"待访问"的坐标
que.push({x, y});
int nextX, nextY;
while (!que.empty()) {
// 首先先将每次的中心坐标取出
pair <int, int> cur = que.front();
// 取出就是访问过,变为海洋
que.pop();
// 查找中心坐标四周的坐标,如果下一个坐标是否条件的,就将坐标push到que中,以待未来取出且作为中心坐标查找周围坐标
for (int i = 0; i < 4; i++) {
nextX = cur.first + direction[i][0];
nextY = cur.second + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
// 如果符合条件,也是访问过,变为海洋
grid[nextX][nextY] = 0;
que.push({nextX, nextY});
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
return 0;
}
6_卡码网102_沉没孤岛
BFS 和 DFS两种 的做法
和101的做法类似,本质上没什么区别
**题目描述
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。
现在你需要将所有孤岛"沉没",即将孤岛中的所有陆地单元格(1)转变为水域单元格(0)。
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。
之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。
输出描述
输出将孤岛"沉没"之后的岛屿矩阵。 注意:每个元素后面都有一个空格
输入示例
4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
输出示例
1 1 0 0 0
1 1 0 0 0
0 0 0 0 0
0 0 0 1 1
cpp
// Date: 2026-02-09 Author:郑龙浩
// 6_卡码网102_沉没孤岛_BFSDFS
// 方法1:DFS 方法2: BFS
// 思路:
// 孤岛定义:位于矩阵内部且不与边缘接触的陆地组成的岛屿
// 和 5_卡码网101_孤岛的总面积 的做法类似,只不过这次边缘岛屿标记为了2
// 1 表示内陆,2 表示边缘陆地,0 表示海洋
// 用时:
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int S = 0;
// DFS作用:深搜陆地,且将搜索到的陆地变为海洋
// 法1.1 隐性的递归终止条件
void dfs1(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
grid[x][y] = 2;
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
// 只要下一个搜索的是 非越界的 && 陆地 就继续以(nextX, nextY) 为中心搜索陆地
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
// grid[nextX][nextY] = 0;
dfs1(grid, nextX, nextY);
}
}
}
// 法1.2 显性递归终止条件(其实就相当于把循环中的条件提到了函数开头)
void dfs2(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
// 如果(x, y) 越界 && 海洋,就return
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0) {
return;
}
grid[x][y] = 2; // 只有(x, y) 没越界 && 是陆地,才标记为2
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
dfs2(grid, nextX, nextY);
}
}
// 法2
void bfs(vector <vector <int>>& grid, int x, int y) {
if (grid[x][y] == 0) { // 如果传过来的参数是海洋,则直接return,不搜索四周的坐标
return;
} else {
grid[x][y] = 2;
}
int height = grid.size();
int width = grid[0].size();
queue <pair <int, int>> que; // 存储"待访问"的坐标
que.push({x, y});
int nextX, nextY;
while (!que.empty()) {
// 首先先将每次的中心坐标取出
pair <int, int> cur = que.front();
// 取出就是访问过,变为海洋
que.pop();
// 查找中心坐标四周的坐标,如果下一个坐标是否条件的,就将坐标push到que中,以待未来取出且作为中心坐标查找周围坐标
for (int i = 0; i < 4; i++) {
nextX = cur.first + direction[i][0];
nextY = cur.second + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
// 如果符合条件,也是访问过,变为海洋
grid[nextX][nextY] = 2;
que.push({nextX, nextY});
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> grid(N, vector <int>(M, 0));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
// 将与左右两列陆地连接的陆地标记为2
for (int i = 0; i < N; i++) {
if (grid[i][0] == 1) bfs(grid, i, 0);
if (grid[i][M - 1] == 1) bfs(grid, i, M - 1);
}
// 将与上下两行陆地连接的陆地标记为2
for (int i = 0; i < M; i++) {
if (grid[0][i] == 1) bfs(grid, 0, i);
if (grid[N - 1][i] == 1) bfs(grid, N - 1, i);
}
// 所有边缘的陆地都标记为了2,所以内陆为1,所以标记为1的陆地都输出为0
for (int i = 0; i < N; i ++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1)
cout << 0 << ' ';
else if (grid[i][j] == 2)
cout << 1 << ' ';
else
cout << 0 << ' ';
}
cout << '\n';
}
return 0;
}