- 第 166 篇 -
Date: 2026 - 02- 11 | 周三
Author: 郑龙浩(仟墨)
2026-02-11 周三 | 算法打卡day6
文章目录
-
- [2026-02-11 周三 | 算法打卡day6](#2026-02-11 周三 | 算法打卡day6)
8_卡码网104_建造最大岛屿
题目描述
给定一个由 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
输出示例
6
提示信息
数据范围:
1 <= M, N <= 50。
cpp
// Date: 2026-02-11 周三 Author:郑龙浩
// 8_卡码网104_建造最大岛屿
// 用时:2 h 11 min
// 大概描述的话就是:
// - 使用dfs/bfs搜索岛屿,且对每个岛屿做不同的编号mark的标记,这个编号是直接写到了grid原数组中的
// - 然后用unordered_map<int ,int> gridS;记录每个岛屿(即编号)的面积:循环便利所有的点,然后girdS[mark]++不断累加每个岛屿的面积
// - 然后遍历所有点,如果遇到了海洋,就然后查找「上下左右」四个方向的陆地,如果是陆地就取出其陆地编号,并利用gridS去查找对应陆地的面积,然后将面积加入到S中,以此类推,去判断四个方向,然后如果是陆地就加上其面积
// - 注意:添加过面积的岛屿不要重复添加
// - 可能会存在比如,该海洋 左边 上边 的陆地是同一个岛屿的情况,此时左边 和 上边 如果都加到S中的话,同一个岛屿的面积就会计算两次
// - 所以为了杜绝这种重复计算同一个岛屿的情况,在寻找四个方向的陆地的时候,需要判断四个方向的陆地是否是同一个岛屿,如果是的话,就只在S中加一次该岛屿的面积
// - 写一个unordered_set <int> visited_mark 记录每个岛屿是否被添加过, 且每次遇到新海洋的时候都要记得clear一下vidited
// 1 遍历所有陆地,用DFS/BFS给每个岛屿编号,并计算每个岛屿的面积,存入grid_mark_S。
// 2 遍历所有海洋,计算如果把它变成陆地,加上周围不同岛屿的面积,得到可能的最大岛屿面积。
// 特殊情况,全是陆地
// 方法1:DFS 方法2: BFS
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
// grid是矩阵,mark是岛屿的编号,cnt是岛屿面积,x, y 是现在的坐标
// 搜索 (x, y) 以及四周的坐标点
void dfs(vector <vector <int>>& grid, int mark, int& cnt, int x, int y) {
int height = grid.size(), width = grid[0].size();
// 如果在传入(x, y)之前有了这个判断,不写下面这一行判断也OK
// 越界 || 海洋 || 编号过(访问过)的岛屿 就不需要搜索了,直接return
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0 || grid[x][y] >= 2) // 为了保险加的,可以去掉这行
return;
cnt++; // (x, y)位置是新的陆地,该岛屿应该++
grid[x][y] = mark; // mark 坐标 (x, y)
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
// 没越界 && 没标记(访问)过的岛屿 就使用dfs深搜周围
if (nextX >= 0 && nextX < height && nextY >=0 && nextY < width && grid[nextX][nextY] == 1)
dfs(grid, mark, cnt, nextX, nextY);
}
}
// 我采用的是 入队就是访问
// 我原来的写的出队就是访问做法,如果少写了pop出来后判断是否访问过的话,就会出现重复访问多算面积的情况,
// 有可能会在入对的时候,入队多次,因为在入队的时候有可能那个坐标已经在que中了只是还没有循环到出队那一步
// 而且que中可能会加入多个相同的坐标, 需要多加一个pop后坐标判断
void bfs(vector <vector <int>>& grid, int mark, int& cnt, int x, int y) {
int height = grid.size(), width = grid[0].size();
// 越界 || 海洋 || 编号过(访问过)的岛屿 就不需要搜索了,直接return
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0 || grid[x][y] >= 2) // 为了保险加的,可以去掉这行
return;
queue <pair <int, int>> que;
que.push({x, y});
grid[x][y] = mark;
cnt++;
while (!que.empty()) {
auto cur = que.front();
que.pop();
int nextX, nextY;
for (int i = 0; i < 4; i ++) {
nextX = cur.first + direction[i][0];
nextY = cur.second + direction[i][1];
// 没越界 && 没标记(访问)过的岛屿 就加入que待搜队列
if (nextX >= 0 && nextX < height && nextY >=0 && nextY < width && grid[nextX][nextY] == 1) {
que.push({nextX, nextY});
cnt++;
grid[nextX][nextY] = mark;
}
}
}
}
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));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j ++) {
cin >> grid[i][j];
}
}
// 1 对所有的岛屿进行编号操作 + 计算每个岛屿的面积
unordered_map <int, int> grid_mark_S; // 岛屿编号 + 岛屿面积
int mark = 2; // 从2开始标记,1是没访问过的陆地,>=2是访问且编号过的陆地,0是海洋
int cnt = 1; // 岛屿面积
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1) { // 如果为1则表示:是岛屿 且 没被编号
cnt = 0; // 我写的dfs搜索的时候,是会搜索(x, y)以及四周,不只是只有四周的,所以面积为0就可以,会默认加上(x, y)的
bfs(grid, mark, cnt, i, j); // 搜索 周围的陆地
grid_mark_S[mark++] = cnt; // 填入面积
}
}
}
// 2 特殊情况,如果地图上全是陆地,没有海洋,第一个循环会给所有岛屿编号并计算面积
// 第二个循环遍历所有海洋,但因为没有海洋(grid[i][j] != 0),所以Max会保持初始值0
// 最后输出0,但正确答案应该是整个地图的面积(如果所有陆地是连通的)或最大岛屿的面积
// 所以加了一个全是陆地判断,以免输出Max = 0
// 解决方案:先把答案Max初始化为所有岛屿中的最大岛屿的面积,然后尝试每个海洋,如果填海能连接出更大的岛屿就更新答案
int Max = 0;
for (const auto& item : grid_mark_S) {
Max = max(Max, item.second);
}
// 3 寻找海洋,尝试计算将海洋变成陆地后的面积
int S = 1; // 面积,海洋变成陆地后的岛屿的面积
unordered_set <int> visited_mark; // 记录已经算过面积的岛屿编号
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 0) { // 如果是海洋的话,就将其变为陆地然后加上周围的面积
visited_mark.clear(); // 每次计算新坐标的时候,都要clear上次坐标周围的岛屿编号
S = 1; // 将该海洋变为陆地,S = 1
// 遍历该海洋周围的岛屿
int nextX, nextY;
for (int k = 0; k < 4; k ++) {
nextX = i + direction[k][0];
nextY = j + direction[k][1];
// 只有没越界,才可以访问(nextX, nextY)
if (nextX >= 0 && nextX < N && nextY >= 0 && nextY < M) {
// 遇到陆地 && 没访问过的岛屿 就 S += gridS[next_mark];
int next_mark = grid[nextX][nextY]; // 下一个坐标点的编号
if (next_mark >= 2 && visited_mark.find(next_mark) == visited_mark.end()) { // 如果没有,返回end
S += grid_mark_S[next_mark];
visited_mark.insert(next_mark); // 标记已访问该岛屿
}
}
}
// 保留最大面积
if (Max < S) Max = S;
}
}
}
cout << Max;
return 0;
}
9_卡码网106_海岸线计算
本题无需用dfs与bfs也能计算出来的,纯迭代做
cpp
// Date: 2026-02-11 周三 Author:郑龙浩
// 9_卡码网106_海岸线计算
// 用时:5min
// 不需要使用DFSBFS也能做,直接遍历判断即可
// 遇到陆地时,就判断四周是否是海洋或者边界,有几个海洋或者陆地就+几
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int cnt = 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];
}
}
// 计算海岸线
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1) {
// 计算每个陆地的海岸线是多少
for (int k = 0; k < 4; k ++) {
int nextX = i + direction[k][0];
int nextY = j + direction[k][1];
// 若越界 或 遇到海洋 都算海洋,然后的都算海岸线
if (nextX < 0 || nextX >= N || nextY < 0 || nextY >= M || grid[nextX][nextY] == 0)
cnt++;
}
}
}
}
cout << cnt;
return 0;
}