news 2026/4/15 4:04:26

代码随想录算法训练营第四十五天:孤岛的总面积,沉没孤岛,高山流水,建造最大工岛

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码随想录算法训练营第四十五天:孤岛的总面积,沉没孤岛,高山流水,建造最大工岛

孤岛的总面积

题目描述

给定一个由 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

提示信息:

在矩阵中心部分的岛屿,因为没有任何一个单元格接触到矩阵边缘,所以该岛屿属于孤岛,总面积为 1。

数据范围:

1 <= M, N <= 50。

思路:

本题要我们找到不靠边的陆地的面积,那么就只需要先从周边找到陆地,通过深搜或者广搜将周边靠陆地并且相邻的陆地全部变成海洋,最后重新遍历,剩下的陆地面积就是我们要求的孤岛面积。

如图,绿色都是接触边缘的岛屿:

在遇到地图周边陆地的时候,将1都变为0,此时地图为这样:

代码示例:

dfs:

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 let count = 0 // 孤岛的总面积 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始深度优先遍历地图 * @param {*} graph 地图 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @return {*} */ const dfs = (graph, x, y) => { if(graph[x][y] === 0) return graph[x][y] = 0 // 标记为海洋 for (let i = 0; i < 4; i++) { let nextx = x + dir[i][0] let nexty = y + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue dfs(graph, nextx, nexty) } } (async function () { // 读取输入,初始化地图 await initGraph() // 遍历地图左右两边 for (let i = 0; i < N; i++) { if (graph[i][0] === 1) dfs(graph, i, 0) if (graph[i][M - 1] === 1) dfs(graph, i, M - 1) } // 遍历地图上下两边 for (let j = 0; j < M; j++) { if (graph[0][j] === 1) dfs(graph, 0, j) if (graph[N - 1][j] === 1) dfs(graph, N - 1, j) } count = 0 // 统计孤岛的总面积 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (graph[i][j] === 1) count++ } } console.log(count); })()

bfs:

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 let count = 0 // 孤岛的总面积 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始广度优先遍历地图 * @param {*} graph 地图 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @return {*} */ const bfs = (graph, x, y) => { let queue = [] queue.push([x, y]) graph[x][y] = 0 // 只要加入队列,立刻标记 while (queue.length) { let [xx, yy] = queue.shift() for (let i = 0; i < 4; i++) { let nextx = xx + dir[i][0] let nexty = yy + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue if (graph[nextx][nexty] === 1) { queue.push([nextx, nexty]) graph[nextx][nexty] = 0 // 只要加入队列,立刻标记 } } } } (async function () { // 读取输入,初始化地图 await initGraph() // 遍历地图左右两边 for (let i = 0; i < N; i++) { if (graph[i][0] === 1) bfs(graph, i, 0) if (graph[i][M - 1] === 1) bfs(graph, i, M - 1) } // 遍历地图上下两边 for (let j = 0; j < M; j++) { if (graph[0][j] === 1) bfs(graph, 0, j) if (graph[N - 1][j] === 1) bfs(graph, N - 1, j) } count = 0 // 统计孤岛的总面积 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (graph[i][j] === 1) count++ } } console.log(count); })()

沉没孤岛

题目描述:

给定一个由 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

提示信息:

将孤岛沉没:

数据范围:

1 <= M, N <= 50

思路:

本题和上一题完全反过来了,上一题是记录地图中间的空格数,本题是要把地图中间的所有1改成0,不过两者在思路上大差不差。

我们还是从地图周边出发,将周边空格相邻的陆地全部标记,然后再遍历一遍地图,遇到没有打标记的陆地,那就是孤岛,全部改成水域即可。

这总共需要三步,先dfs或者bfs将所有的1改成2,将中间的1改成0,再把之前所有的2改回1

如图:

代码示例:

dfs

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始深度优先遍历地图 * @param {*} graph 地图 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @return {*} */ const dfs = (graph, x, y) => { if (graph[x][y] !== 1) return graph[x][y] = 2 // 标记为非孤岛陆地 for (let i = 0; i < 4; i++) { let nextx = x + dir[i][0] let nexty = y + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue dfs(graph, nextx, nexty) } } (async function () { // 读取输入,初始化地图 await initGraph() // 遍历地图左右两边 for (let i = 0; i < N; i++) { if (graph[i][0] === 1) dfs(graph, i, 0) if (graph[i][M - 1] === 1) dfs(graph, i, M - 1) } // 遍历地图上下两边 for (let j = 0; j < M; j++) { if (graph[0][j] === 1) dfs(graph, 0, j) if (graph[N - 1][j] === 1) dfs(graph, N - 1, j) } // 遍历地图,将孤岛沉没,即将陆地1标记为0;将非孤岛陆地2标记为1 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (graph[i][j] === 1) graph[i][j] = 0 else if (graph[i][j] === 2) graph[i][j] = 1 } console.log(graph[i].join(' ')); } })() #

bfs

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始广度优先遍历地图 * @param {*} graph 地图 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @return {*} */ const bfs = (graph, x, y) => { let queue = [] queue.push([x, y]) graph[x][y] = 2 // 标记为非孤岛陆地 while (queue.length) { let [xx, yy] = queue.shift() for (let i = 0; i < 4; i++) { let nextx = xx + dir[i][0] let nexty = yy + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue if (graph[nextx][nexty] === 1) bfs(graph, nextx, nexty) } } } (async function () { // 读取输入,初始化地图 await initGraph() // 遍历地图左右两边 for (let i = 0; i < N; i++) { if (graph[i][0] === 1) bfs(graph, i, 0) if (graph[i][M - 1] === 1) bfs(graph, i, M - 1) } // 遍历地图上下两边 for (let j = 0; j < M; j++) { if (graph[0][j] === 1) bfs(graph, 0, j) if (graph[N - 1][j] === 1) bfs(graph, N - 1, j) } // 遍历地图,将孤岛沉没,即将陆地1标记为0;将非孤岛陆地2标记为1 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (graph[i][j] === 1) graph[i][j] = 0 else if (graph[i][j] === 2) graph[i][j] = 1 } console.log(graph[i].join(' ')); } })()

水流问题

题目描述:

现有一个 N × M 的矩阵,每个单元格包含一个数值,这个数值代表该位置的相对高度。矩阵的左边界和上边界被认为是第一组边界,而矩阵的右边界和下边界被视为第二组边界。

矩阵模拟了一个地形,当雨水落在上面时,水会根据地形的倾斜向低处流动,但只能从较高或等高的地点流向较低或等高并且相邻(上下左右方向)的地点。我们的目标是确定那些单元格,从这些单元格出发的水可以达到第一组边界和第二组边界。

输入描述:

第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。

后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。

输出描述:

输出共有多行,每行输出两个整数,用一个空格隔开,表示可达第一组边界和第二组边界的单元格的坐标,输出顺序任意。

输入示例:

5 5 1 3 1 2 4 1 2 1 3 2 2 4 7 2 1 4 5 6 1 1 1 4 1 2 1

输出示例:

0 4 1 3 2 2 3 0 3 1 3 2 4 0 4 1

提示信息:

图中的蓝色方块上的雨水既能流向第一组边界,也能流向第二组边界。所以最终答案为所有蓝色方块的坐标。

数据范围:

1 <= M, N <= 50

思路:

我们可以先从第一组边界上的节点逆流而上,将遍历过的节点全部标记上,然后再从第二组边界出发,进行同样的操作,双方都标记过的节点就是既可以流向第一组边界又可以流向第二组边界的节点。如图:

有红又有绿的就是我们要求的节点

代码示例:

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始深度优先遍历地图 * @param {*} graph 地图 * @param {*} visited 可访问节点 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @return {*} */ const dfs = (graph, visited, x, y) => { if (visited[x][y]) return visited[x][y] = true // 标记为可访问 for (let i = 0; i < 4; i++) { let nextx = x + dir[i][0] let nexty = y + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界,跳过 if (graph[x][y] < graph[nextx][nexty]) continue //不能流过.跳过 dfs(graph, visited, nextx, nexty) } } /** * @description: 判断地图上的(x, y)是否可以到达第一组边界和第二组边界 * @param {*} x 坐标 * @param {*} y 坐标 * @return {*} true可以到达,false不可以到达 */ const isResult = (x, y) => { let visited = new Array(N).fill(false).map(() => new Array(M).fill(false)) let isFirst = false //是否可到达第一边界 let isSecond = false //是否可到达第二边界 // 深搜,将(x, y)可到达的所有节点做标记 dfs(graph, visited, x, y) // 判断能否到第一边界左边 for (let i = 0; i < N; i++) { if (visited[i][0]) { isFirst = true break } } // 判断能否到第一边界上边 for (let j = 0; j < M; j++) { if (visited[0][j]) { isFirst = true break } } // 判断能否到第二边界右边 for (let i = 0; i < N; i++) { if (visited[i][M - 1]) { isSecond = true break } } // 判断能否到第二边界下边 for (let j = 0; j < M; j++) { if (visited[N - 1][j]) { isSecond = true break } } return isFirst && isSecond } (async function () { // 读取输入,初始化地图 await initGraph() // 遍历地图,判断是否能到达第一组边界和第二组边界 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (isResult(i, j)) console.log(i + ' ' + j); } } })()

建造最大岛屿

题目描述:

给定一个由 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

提示信息

对于上面的案例,有两个位置可将 0 变成 1,使得岛屿的面积最大,即 6。

数据范围:

1 <= M, N <= 50。

思路:

本题的暴力解法是将每一个0改成1,但是其实我们只需要用一次dfs把每个岛屿的面积记录下来就好,然后遍历所有0的方格(把0变成1),然后统计该1(0变成的1)周边岛屿面具,将相邻面积加起来,遍历所有0之后,就可以得到一个选一个0变成1之后的最大面积。

代码示例:

const r1 = require('readline').createInterface({ input: process.stdin }); // 创建readline接口 let iter = r1[Symbol.asyncIterator](); // 创建异步迭代器 const readline = async () => (await iter.next()).value; let graph // 地图 let N, M // 地图大小 let visited // 访问过的节点, 标记岛屿 const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向 let count = 0 // 统计岛屿面积 let areaMap = new Map() // 存储岛屿面积 // 读取输入,初始化地图 const initGraph = async () => { let line = await readline(); [N, M] = line.split(' ').map(Number); graph = new Array(N).fill(0).map(() => new Array(M).fill(0)) visited = new Array(N).fill(0).map(() => new Array(M).fill(0)) for (let i = 0; i < N; i++) { line = await readline() line = line.split(' ').map(Number) for (let j = 0; j < M; j++) { graph[i][j] = line[j] } } } /** * @description: 从(x,y)开始深度优先遍历地图 * @param {*} graph 地图 * @param {*} visited 可访问节点 * @param {*} x 开始搜索节点的下标 * @param {*} y 开始搜索节点的下标 * @param {*} mark 当前岛屿的标记 * @return {*} */ const dfs = (graph, visited, x, y, mark) => { if (visited[x][y] != 0) return visited[x][y] = mark count++ for (let i = 0; i < 4; i++) { let nextx = x + dir[i][0] let nexty = y + dir[i][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界, 跳过 // 已访问过, 或者是海洋, 跳过 if (visited[nextx][nexty] != 0 || graph[nextx][nexty] == 0) continue dfs(graph, visited, nextx, nexty, mark) } } (async function () { // 读取输入,初始化地图 await initGraph() let isAllLand = true //标记整个地图都是陆地 let mark = 2 // 标记岛屿 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (graph[i][j] == 0 && isAllLand) isAllLand = false if (graph[i][j] === 1 && visited[i][j] === 0) { count = 0 dfs(graph, visited, i, j, mark) areaMap.set(mark, count) mark++ } } } // 如果全是陆地, 直接返回面积 if (isAllLand) { console.log(N * M); return } let result = 0 // 记录最后结果 let visitedIsland = new Map() //标记访问过的岛屿, 因为海洋四周可能是同一个岛屿, 需要标记避免重复统计面积 for (let i = 0; i < N; i++) { for (let j = 0; j < M; j++) { if (visited[i][j] === 0) { count = 1 // 记录连接之后的岛屿数量 visitedIsland.clear() // 每次使用时,清空 // 计算海洋周围岛屿面积 for (let m = 0; m < 4; m++) { const nextx = i + dir[m][0] const nexty = j + dir[m][1] if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue //越界, 跳过 const island = visited[nextx][nexty] if (island == 0 || visitedIsland.get(island)) continue// 四周是海洋或者访问过的陆地 跳过 // 标记为访问, 计算面积 visitedIsland.set(island, true) count += areaMap.get(visited[nextx][nexty]) } result = Math.max(result, count) } } } console.log(result); })()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 9:25:53

线性代数(十)四个基本子空间与矩阵空间

对于一个矩阵A&#xff08;m行n列&#xff09;&#xff0c;可以找到其对应的四个子空间&#xff0c;它们分别是&#xff1a; 1.列空间&#xff0c;C(A)&#xff1b;因为每个列向量是m维的&#xff0c;所以C&#xff08;A&#xff09;是的子空间2.零空间&#xff0c;N&#xff0…

作者头像 李华
网站建设 2026/4/10 23:03:31

Docker Volume持久化保存PyTorch训练数据

Docker Volume持久化保存PyTorch训练数据 在深度学习项目中&#xff0c;模型训练往往需要数小时甚至数天时间。你是否经历过这样的场景&#xff1a;训练到第80个epoch时&#xff0c;容器意外退出&#xff0c;所有中间结果瞬间丢失&#xff1f;或者团队成员因为环境差异导致“在…

作者头像 李华
网站建设 2026/4/9 6:35:34

Dify平台接入自定义PyTorch模型,实现私有化部署

Dify平台接入自定义PyTorch模型&#xff0c;实现私有化部署 在企业级AI应用落地的过程中&#xff0c;一个日益突出的矛盾正摆在开发者面前&#xff1a;大模型平台提供了强大的交互能力与低代码开发体验&#xff0c;但其默认依赖的公有云模型却难以满足金融、医疗、制造等行业对…

作者头像 李华
网站建设 2026/4/12 20:07:22

Jupyter Notebook自动保存设置,防止PyTorch训练中断丢失

Jupyter Notebook自动保存设置&#xff0c;防止PyTorch训练中断丢失 在深度学习项目中&#xff0c;最令人沮丧的场景之一莫过于&#xff1a;经过十几个小时的模型训练后&#xff0c;系统突然断连&#xff0c;而你发现最新的代码和日志都没保存下来。尤其是当你在远程云服务器上…

作者头像 李华
网站建设 2026/4/10 3:19:11

PyTorch-CUDA镜像内置JupyterLab,支持插件扩展

PyTorch-CUDA镜像内置JupyterLab&#xff0c;支持插件扩展 在深度学习项目开发中&#xff0c;最令人头疼的往往不是模型设计本身&#xff0c;而是环境搭建——明明代码写好了&#xff0c;却因为CUDA版本不匹配、PyTorch安装失败、Jupyter内核无法启动等问题卡住一整天。这种“在…

作者头像 李华
网站建设 2026/4/14 2:58:07

Markdown文档记录PyTorch实验日志,提升科研效率

使用 PyTorch-CUDA 镜像与 Markdown 提升科研效率 在深度学习研究中&#xff0c;一个常见的尴尬场景是&#xff1a;你在本地训练好的模型&#xff0c;换到同事的机器上却跑不起来——报错信息五花八门&#xff0c;从 CUDA 版本不兼容到 PyTorch API 变更&#xff0c;问题层出不…

作者头像 李华