当前位置: 首页 > news >正文

算法训练营day52 图论③ 101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿

        图论部分,继续岛屿问题

101.孤岛的总面积

        本题较为简单,可以增加一个预处理

        本题要求找到不靠边的陆地面积,那么我们只要从周边找到陆地然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地都变成海洋,然后再去重新遍历地图 统计此时还剩下的陆地就可以了。

深度搜索

        深度搜索中将岛屿标记进行修改来减少重复遍历的行为,但是这样破坏了数据的一致性,可以改为使用visit数组来判断,然后判断的时候可以不用同时使用,会造成冗余

position = [[1, 0], [0, 1], [-1, 0], [0, -1]]
count = 0def dfs(grid, x, y):global countgrid[x][y] = 0count += 1for i, j in position:next_x = x + inext_y = y + jif next_x < 0 or next_y < 0 or next_x >= len(grid) or next_y >= len(grid[0]):continueif grid[next_x][next_y] == 1: dfs(grid, next_x, next_y)n, m = map(int, input().split())# 邻接矩阵
grid = []
for i in range(n):grid.append(list(map(int, input().split())))# 清除边界上的连通分量
for i in range(n):if grid[i][0] == 1: dfs(grid, i, 0)if grid[i][m - 1] == 1: dfs(grid, i, m - 1)for j in range(m):if grid[0][j] == 1: dfs(grid, 0, j)if grid[n - 1][j] == 1: dfs(grid, n - 1, j)count = 0 # 将count重置为0
# 统计内部所有剩余的连通分量
for i in range(n):for j in range(m):if grid[i][j] == 1:dfs(grid, i, j)print(count)

广度搜索

        广度搜索中使用队列来实现

from collections import deque# 处理输入
n, m = list(map(int, input().split()))
g = []
for _ in range(n):row = list(map(int, input().split()))g.append(row)# 定义四个方向、孤岛面积(遍历完边缘后会被重置)
directions = [[0,1], [1,0], [-1,0], [0,-1]]
count = 0# 广搜
def bfs(r, c):global countq = deque()q.append((r, c))g[r][c] = 0count += 1while q:r, c = q.popleft()for di in directions:next_r = r + di[0]next_c = c + di[1]if next_c < 0 or next_c >= m or next_r < 0 or next_r >= n:continueif g[next_r][next_c] == 1:q.append((next_r, next_c))g[next_r][next_c] = 0count += 1for i in range(n):if g[i][0] == 1: bfs(i, 0)if g[i][m-1] == 1: bfs(i, m-1)for i in range(m):if g[0][i] == 1: bfs(0, i)if g[n-1][i] == 1: bfs(n-1, i)count = 0
for i in range(n):for j in range(m):if g[i][j] == 1: bfs(i, j)print(count)

102.沉没孤岛

        仍然是预处理的办法        

        思路依然是从地图周边出发,将周边空格相邻的陆地都做上标记,然后在遍历一遍地图,遇到 陆地 且没做过标记的,那么都是地图中间的 陆地 ,全部改成水域就行。

深度搜索

        整体思路不难,但是需要注意,对于矩阵的读入和写出问题

        字符串的 join 方法只能接收由字符串组成的可迭代对象(如字符串列表),所以需要map函数转换格式

def dfs(grid, x, y):grid[x][y] = 2directions = [(-1, 0), (0, -1), (1, 0), (0, 1)]  # 四个方向for dx, dy in directions:nextx, nexty = x + dx, y + dy# 超过边界if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):continue# 不符合条件,不继续遍历if grid[nextx][nexty] == 0 or grid[nextx][nexty] == 2:continuedfs(grid, nextx, nexty)def main():n, m = map(int, input().split())grid = [[int(x) for x in input().split()] for _ in range(n)]# [int(x) for x in input().split()] 每行元素逐个改为int类型# 步骤一:# 从左侧边,和右侧边 向中间遍历for i in range(n):if grid[i][0] == 1:dfs(grid, i, 0)if grid[i][m - 1] == 1:dfs(grid, i, m - 1)# 从上边和下边 向中间遍历for j in range(m):if grid[0][j] == 1:dfs(grid, 0, j)if grid[n - 1][j] == 1:dfs(grid, n - 1, j)# 步骤二、步骤三for i in range(n):for j in range(m):if grid[i][j] == 1:grid[i][j] = 0if grid[i][j] == 2:grid[i][j] = 1# 打印结果for row in grid:print(' '.join(map(str, row)))if __name__ == "__main__":main()

103.水流问题

        这道题的普通思路就是遍历每个节点,对每个节点同时做一二边界的判断,遍历每一个节点,是 m * n,遍历每一个节点的时候,都要做深搜,深搜的时间复杂度是: m * n,那么整体时间复杂度 就是 O(m^2 * n^2) ,这是一个四次方的时间复杂度。

        优化的思路是逆向思维,可以从边界出发,一二边界同时覆盖的就是本题所求,因为visit数组的存在,会让重复的位置不会走第二次,这一点会对效率带来很大提升,所以本题整体的时间复杂度其实是: 2 * n * m + n * m ,所以最终时间复杂度为 O(n * m) 。(具体解释略)

        本题的关键地方在于增加对图论中搜索算法的时间复杂度分析

        strip():移除字符串开头和结尾的空白字符(可以删去)

first = set()
second = set() # 保护性设置directions = [[-1, 0], [0, 1], [1, 0], [0, -1]]def dfs(i, j, graph, visited, side):if visited[i][j]:returnvisited[i][j] = Trueside.add((i, j))for x, y in directions:new_x = i + xnew_y = j + yif (0 <= new_x < len(graph)and 0 <= new_y < len(graph[0])and int(graph[new_x][new_y]) >= int(graph[i][j])):dfs(new_x, new_y, graph, visited, side)def main():global firstglobal secondN, M = map(int, input().strip().split())graph = []for _ in range(N):row = input().strip().split()graph.append(row)# 是否可到达第一边界visited = [[False] * M for _ in range(N)]for i in range(M):dfs(0, i, graph, visited, first)for i in range(N):dfs(i, 0, graph, visited, first)# 是否可到达第二边界visited = [[False] * M for _ in range(N)]for i in range(M):dfs(N - 1, i, graph, visited, second)for i in range(N):dfs(i, M - 1, graph, visited, second)# 可到达第一边界和第二边界res = first & second  # 取交集for x, y in res:print(f"{x} {y}")if __name__ == "__main__":main()

104.建造最大岛屿

        这道题是有难度的,因为没有一个很直观的解法

        仍然是预处理的逻辑,剩下的内容就是遍历每个元素,相加周围四个数值即可

深度遍历

        需要注意visit数组不可缺少!防止存在两个相邻的同片岛屿重复相加

from typing import List  # 导入类型提示工具,用于指定参数类型
from collections import defaultdict  # 导入默认字典,方便统计岛屿面积class Solution:def __init__(self):# 定义四个方向的偏移量(下、上、右、左),用于遍历相邻单元格self.direction = [(1,0), (-1,0), (0,1), (0,-1)]self.res = 0  # 记录将海洋(0)转为陆地(1)后,能连接的最大岛屿总面积self.count = 0  # 临时计数:当前岛屿包含的单元格数量self.idx = 1  # 岛屿编号:从2开始(避免与原始的1/0冲突)# 用默认字典存储每个岛屿的面积:键是岛屿编号,值是该岛屿的单元格总数self.count_area = defaultdict(int)def max_area_island(self, grid: List[List[int]]) -> int:"""主函数:计算最大岛屿面积(含转换一个海洋为陆地后的可能情况)"""# 边界检查:如果网格为空,直接返回0if not grid or len(grid) == 0 or len(grid[0]) == 0:return 0# 第一步:遍历网格,用DFS标记所有独立岛屿(分配唯一编号)for i in range(len(grid)):  # 遍历每一行for j in range(len(grid[0])):  # 遍历每一列if grid[i][j] == 1:  # 发现未标记的陆地(原始值为1)self.count = 0  # 重置当前岛屿的计数self.idx += 1  # 岛屿编号+1(从2开始)self.dfs(grid, i, j)  # 深度优先搜索,标记整个岛屿# 第二步:统计每个岛屿的面积(单元格数量)self.check_area(grid)# 第三步:检查每个海洋单元格,计算转换后能连接的最大面积# 如果存在可连接的海洋(has_connect为True),返回"最大连接面积+1"(+1是转换的那个单元格)# 否则返回原始最大岛屿面积if self.check_largest_connect_island(grid=grid):return self.res + 1# 如果没有海洋(全是陆地),返回最大岛屿面积(注意排除0的统计)return max(self.count_area.values()) if self.count_area else 0def dfs(self, grid, row, col):"""深度优先搜索:标记岛屿并统计单元格数量"""# 将当前单元格标记为当前岛屿编号(覆盖原始的1)grid[row][col] = self.idxself.count += 1  # 该岛屿的单元格数量+1# 遍历四个方向的相邻单元格for dr, dc in self.direction:_row = dr + row  # 计算相邻单元格的行坐标_col = dc + col  # 计算相邻单元格的列坐标# 检查相邻单元格是否在网格范围内,且是未标记的陆地(值为1)if 0 <= _row < len(grid) and 0 <= _col < len(grid[0]) and grid[_row][_col] == 1:self.dfs(grid, _row, _col)  # 递归遍历相邻陆地returndef check_area(self, grid):"""统计每个岛屿编号对应的单元格数量(即岛屿面积)"""m, n = len(grid), len(grid[0])  # 获取网格的行数和列数# 遍历网格的每个单元格for row in range(m):for col in range(n):# 以单元格的值(岛屿编号或0)为键,累加出现次数(即面积)# get方法:如果键不存在,返回0;存在则返回当前值,然后+1self.count_area[grid[row][col]] = self.count_area.get(grid[row][col], 0) + 1returndef check_largest_connect_island(self, grid):"""检查每个海洋单元格,计算转换为陆地后能连接的最大岛屿面积"""m, n = len(grid), len(grid[0])has_connect = False  # 标记是否存在海洋单元格(0)# 遍历网格中的每个单元格for row in range(m):for col in range(n):if grid[row][col] == 0:  # 找到一个海洋单元格has_connect = True  # 标记存在可转换的海洋area = 0  # 临时存储当前海洋能连接的岛屿总面积visited = set()  # 记录已统计过的岛屿编号(避免重复计算)# 检查该海洋单元格的四个相邻方向for dr, dc in self.direction:_row = row + dr  # 相邻单元格的行坐标_col = col + dc  # 相邻单元格的列坐标# 检查相邻单元格是否在网格内,且是岛屿(非0),且未统计过if (0 <= _row < len(grid) and 0 <= _col < len(grid[0]) and grid[_row][_col] != 0 and grid[_row][_col] not in visited):visited.add(grid[_row][_col])  # 标记该岛屿已统计# 累加该岛屿的面积(从count_area字典中获取)area += self.count_area[grid[_row][_col]]# 更新最大连接面积self.res = max(self.res, area)return has_connect  # 返回是否存在海洋单元格def main():# 读取输入:第一行是网格的行数(m)和列数(n)m, n = map(int, input().split())grid = []# 读取m行数据,每行转换为整数列表,组成网格for i in range(m):grid.append(list(map(int, input().split())))# 创建Solution实例,计算并打印结果sol = Solution()print(sol.max_area_island(grid))if __name__ == '__main__':main()
http://www.xdnf.cn/news/1310185.html

相关文章:

  • 基于Uni-app+vue3实现微信小程序地图固定中心点范围内拖拽选择位置功能(分步骤详解)
  • MySQL 配置性能优化赛技术文章
  • 基于Python3.10.6与jieba库的中文分词模型接口在Windows Server 2022上的实现与部署教程
  • Flutter开发 网络请求
  • ESP32-S3_ES8311音频输出使用
  • 【嵌入式C语言】六
  • 【读论文】医疗AI大模型:百川开源Baichuan-M2
  • 第二十五天:构造函数/析构函数/拷贝构造
  • 开发一款多商户电商APP要多久?功能拆解与源码技术落地方案
  • 迭代器模式及优化
  • 模式匹配自动机全面理论分析
  • 【Web后端】Django、flask及其场景——以构建系统原型为例
  • AI 搜索时代:引领变革,重塑您的 SEO 战略
  • 基于uni-app+vue3实现的微信小程序地图范围限制与单点标记功能实现指南
  • Matplotlib直线绘制:从基础到三维空间的高级可视化
  • 数组名本质与指针运算揭秘
  • List容器:特性与操作使用指南
  • 零基础学习人工智能的完整路线规划
  • 民法学学习笔记(个人向) Part.5
  • 学习游戏制作记录(制作系统与物品掉落系统)8.16
  • MySQL查询性能慢时索引失效的排查与优化实践
  • Redis缓存
  • 【OpenGL】LearnOpenGL学习笔记09 - 材质、光照贴图
  • 登录与登录校验:Web安全核心解析
  • 【昇腾】单张48G Atlas 300I Duo推理卡MindIE+WebUI方式跑7B大语言模型_20250816
  • 如何在FastAPI中玩转APScheduler,实现动态定时任务的魔法?
  • 【wmi异常】关于taskkill命令提示“错误:找不到” 以及无法正常获取设备机器码的处理办法
  • pytorch例子计算两张图相似度
  • PHP反序列化的CTF题目环境和做题复现第2集_POP链构造
  • 利用Qwen大模型进行c++11并发库的学习,与时俱进!!!!