首页 > 其他分享 >第十二章 图论 Part3

第十二章 图论 Part3

时间:2024-09-06 13:35:57浏览次数:5  
标签:图论 岛屿 第十二章 range Part3 grid visited nextX nextY

目录

任务

Kama101. 孤岛的总面积

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。

现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。

思路

只有孤岛才计算面积,所以先遍历边缘岛,将这些不符合条件的格子遍历并标记后,再遍历孤岛并计算。

# 处理输入
n, m = map(int, input().strip().split())
grid = []
for _ in range(n):
    row = list(map(int, input().strip().split()))
    grid.append(row)

count = 0
def dfs(grid,visited,x,y):
    global count
    dir = [(1,0),(0,1),(-1,0),(0,-1)] #逆时针四个方向
    visited[x][y] = True # 由调用者(包含外部和自身)保证了该格子为访问过的陆地!
    count += 1
    
    for i in range(4):
        nextX = x + dir[i][0]
        nextY = y + dir[i][1]
    
        if nextX < 0 or nextY<0 or nextX>=len(grid) or nextY>=len(grid[0]):
            continue
        if visited[nextX][nextY] == False and grid[nextX][nextY] == 1: #未访问过的陆地则继续访问(直到遍历完整个单独的岛屿)
            dfs(grid,visited,nextX,nextY)
            
visited = [[False]*m for _ in range(n)]

# 将边缘岛的所有陆地格子标记为已访问(从边缘开始dfs遍历)
for j in range(m):
    if grid[0][j] == 1 and not visited[0][j]:
        dfs(grid,visited,0,j) 
    if grid[n-1][j]==1 and not visited[n-1][j]:
        dfs(grid,visited,n-1,j)
for i in range(n):
    if grid[i][0] == 1 and not visited[i][0]:
        dfs(grid,visited,i,0)
    if grid[i][m-1] == 1 and not visited[i][m-1]:    
        dfs(grid,visited,i,m-1)

#遍历中间格子并累计值
sum = 0
for i in range(1,n-1):
    for j in range(1,m-1):
        if visited[i][j]==False and grid[i][j] ==1:
            count = 0
            dfs(grid,visited,i,j)
            sum += count
print(sum)
    

Kama102. 沉没孤岛

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。

现在你需要将所有孤岛“沉没”,即将孤岛中的所有陆地单元格(1)转变为水域单元格(0)。

思路

同样的,将边缘岛遍历并标记,然后将其他的1变成0,即符合题意(中间的所有1均为孤岛)

# 处理输入
n, m = map(int, input().strip().split())
grid = []
for _ in range(n):
    row = list(map(int, input().strip().split()))
    grid.append(row)

count = 0
def dfs(grid,visited,x,y):
    dir = [(1,0),(0,1),(-1,0),(0,-1)] #逆时针四个方向
    visited[x][y] = True # 由调用者(包含外部和自身)保证了该格子为访问过的陆地!
    
    for i in range(4):
        nextX = x + dir[i][0]
        nextY = y + dir[i][1]
    
        if nextX < 0 or nextY<0 or nextX>=len(grid) or nextY>=len(grid[0]):
            continue
        if visited[nextX][nextY] == False and grid[nextX][nextY] == 1: #未访问过的陆地则继续访问(直到遍历完整个单独的岛屿)
            dfs(grid,visited,nextX,nextY)
            
visited = [[False]*m for _ in range(n)]

# 将边缘岛的所有陆地格子标记为已访问(从边缘开始dfs遍历)
for j in range(m):
    if grid[0][j] == 1 and not visited[0][j]:
        dfs(grid,visited,0,j) 
    if grid[n-1][j]==1 and not visited[n-1][j]:
        dfs(grid,visited,n-1,j)
for i in range(n):
    if grid[i][0] == 1 and not visited[i][0]:
        dfs(grid,visited,i,0)
    if grid[i][m-1] == 1 and not visited[i][m-1]:    
        dfs(grid,visited,i,m-1)

#遍历中间格子并将孤岛修改为水
sum = 0
for i in range(1,n-1):
    for j in range(1,m-1):
        if visited[i][j]==False and grid[i][j] ==1:
            grid[i][j] = 0

for i in range(n):
    for j in range(m):
        print(grid[i][j],end = ' ')
    print()

Kama103. 水流问题

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

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

思路

以边界为起点,统计所有可以遍历到的节点,如果是第一,第二边界都能统计到的点,为符合题意的点,输出即可。

# 处理输入
n, m = map(int, input().strip().split())
grid = []
for _ in range(n):
    row = list(map(int, input().strip().split()))
    grid.append(row)
    
dir = [(1,0),(0,1),(-1,0),(0,-1)] #逆时针四个方向

count = 0 # 单个岛屿的面积
def dfs(grid,visited,x,y):
    global dir
    visited[x][y] = True # 由调用者(包含外部和自身)保证了该格子为访问过的陆地!
    
    for i in range(4):
        nextX = x + dir[i][0]
        nextY = y + dir[i][1]
    
        if nextX < 0 or nextY<0 or nextX>=len(grid) or nextY>=len(grid[0]):
            continue
        if not visited[nextX][nextY] and grid[x][y] <= grid[nextX][nextY]: #从低往高遍历所能达到的所有节点,记录在visited中
            dfs(grid,visited,nextX,nextY)
    
first = [[False]*m for _ in range(n)]
second = [[False]*m for _ in range(n)]

#从边界开始遍历,标记所有遍历过的节点
for j in range(m):
    dfs(grid,first,0,j)
    dfs(grid,second,n-1,j)
for i in range(n):
    dfs(grid,first,i,0)
    dfs(grid,second,i,m-1)

mySet = set()
for i in range(n):
    for j in range(m):
        if first[i][j] and second[i][j]: #如果即能被第一边界遍历到,又能被第二边界遍历到,则满足条件
            # print(i,end = ' ')
            # print(j,end = ' ')
            # print()
            print(f"{i} {j}")


Kama104. 建造最大岛屿

给定一个由 1(陆地)和 0(水)组成的矩阵,你最多可以将矩阵中的一格水变为一块陆地,在执行了此操作之后,矩阵中最大的岛屿面积是多少。

岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设矩阵外均被水包围。

思路

将某个水格子变为陆地格子后,可能与多个(>=0)原岛屿连接形成新岛屿。对于每个水格子,四方向上判断有无陆地格子,还要判断陆地格子是否属于同一岛屿,不能重复添加,符合条件后,累加岛屿的面积。
使用一个map更方便的进行统计,key为岛屿索引,value为岛屿面积。岛屿索引从2开始(因为给定输入所有陆地格子为1,遍历时需要用这个判断是否为陆地)。每次遍历完一个岛屿,岛屿索引+1。
此外使用一个set来判断水格子周围的陆地格子是否属于同一岛屿(是否已经加过相同岛屿面积)
另外注意别忘记了给定输入全是陆地的特殊情况
实际上这里因为有了岛屿索引,所以不需要visited这个二维列表来记录和判断了,但是为了逻辑的清晰,下面的实际实现还是使用这个变量

# 处理输入
n, m = map(int, input().strip().split())
grid = []
for _ in range(n):
    row = list(map(int, input().strip().split()))
    grid.append(row)
    
dir = [(1,0),(0,1),(-1,0),(0,-1)] #逆时针四个方向

count = 0 # 单个岛屿的面积
islandIndex = 2 #因为初始时1表示陆地,所以岛屿编号从2开始
def dfs(grid,visited,x,y):
    global count
    global islandIndex
    global dir
    visited[x][y] = True # 由调用者(包含外部和自身)保证了该格子为访问过的陆地!
    grid[x][y] = islandIndex
    count += 1
    
    
    for i in range(4):
        nextX = x + dir[i][0]
        nextY = y + dir[i][1]
    
        if nextX < 0 or nextY<0 or nextX>=len(grid) or nextY>=len(grid[0]):
            continue
        if visited[nextX][nextY] == False and grid[nextX][nextY] == 1: #未访问过的陆地则继续访问(直到遍历完整个单独的岛屿)
            dfs(grid,visited,nextX,nextY)
            
visited = [[False]*m for _ in range(n)]
# 记录岛屿索引和对应面积
islandMap = {}
for i in range(n):
    for j in range(m):
        if not visited[i][j] and grid[i][j] == 1:
            count = 0
            dfs(grid,visited,i,j)
            islandMap[islandIndex] = count
            islandIndex += 1

#尝试每次将一个水格子变成陆地
result = 0
isAllLand = True
for i in range(n):
    for j in range(m):
        nowArea = 1 #连接后的岛屿面积,默认为1表示修改水域后未连接其他岛屿
        visitedIsland = set() #保存已访问过的岛屿索引
        if grid[i][j] == 0: 
            isAllLand = False
            for k in range(4): #四方向上看有没有陆地
                nextX = i + dir[k][0]
                nextY = j + dir[k][1]
                if nextX < 0 or nextY<0 or nextX>=len(grid) or nextY>=len(grid[0]):
                    continue
                if grid[nextX][nextY] in visitedIsland:
                    continue
                
                if grid[nextX][nextY] in islandMap:
                    nowArea += islandMap[grid[nextX][nextY]]
                    visitedIsland.add(grid[nextX][nextY])
        result = max(result,nowArea)
if isAllLand:
    print(n*m)
else:
    print(result) 

心得

dfs中的递归深入条件和bfs中的加入队列的条件都是重点,一般是根据题意要求来进行深搜或广搜,对于要求遍历整个图的问题,我们使用的就是visited来访问哪些还未访问过的节点,而对于更细节的题目,比如岛屿问题,水流问题等,深入(或广度)遍历的条件还要加上题目的需求(比如是否是陆地,是否往高处等),此外还要注意从外部初次进入到dfs或bfs的初始情况,比如岛屿问题初次进入时也是必须以陆地格子为起点等。

标签:图论,岛屿,第十二章,range,Part3,grid,visited,nextX,nextY
From: https://www.cnblogs.com/haohaoscnblogs/p/18400019

相关文章

  • 代码随想录day52 || 图论3
    岛屿最大的孤岛面积packagemainimport"fmt"vardirPath=[4][2]int{{0,-1},{1,0},{0,1},{-1,0}}varvisited[][]boolvarflagboolvarresintfuncmain(){ varx,yint fmt.Scanf("%d%d",&x,&y) //x行y列初始化临界矩阵 vargra......
  • 第十一章 图论 Part2
    目录任务200.岛屿数量思路695.岛屿的最大面积思路任务200.岛屿数量给你一个由'1'(陆地)和'0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。思......
  • 代码随想录day52 || 图论搜索 岛屿数量,岛屿的最大面积
    图遍历dfs深度优先搜索bfs广度优先搜索200岛屿数量(dfs)vardirPath=[][]int{{0,-1},{1,0},{0,1},{-1,0}}//上,右,下,左varvisited[][]boolfuncnumIslands(grid[][]byte)int{ //dfs深度优先遍历,对于每一个节点,按照上下左右四个固定顺序遍历,然后到下......
  • 搜索与图论
    这部分内容要用到树的数据结构,我们有以下几种方式来存储节点邻接表邻接表就是用类似链表的结构来存储数据,先创建一个数组h,每一个位置都有一个表头。然后e数组和ne数组分别表示节点的值是多少,节点的下一个节点的编号是多少,这种方式一般用在稠密图中,也就是节点数跟边数相差很大。......
  • 图论连通性相关
    并查集普通并查集:路径压缩写法:structUnion_Find_Set{ intf[N]; inlinevoidinit(){ for(inti=1;i<=n;++i) f[i]=i; } inlineintfind(intx){ if(x!=f[x])f[x]=find(f[x]); returnf[x]; } inlinevoidmerge(inta,intb){ intx......
  • 代码随想录训练营 Day50打卡 图论part01 理论基础 98. 所有可达路径
    代码随想录训练营Day50打卡图论part01一、理论基础DFS(深度优先搜索)和BFS(广度优先搜索)在图搜索中的核心区别主要体现在搜索策略上:1、搜索方向:DFS:深度优先,一条路走到黑。DFS从起点开始,选择一个方向深入到底,遇到死胡同再回溯,换一个方向继续探索。这种方式适合解决路径......
  • [Python图论]在用图nx.shortest_path求解最短路径时,节点之间有多条边edge,会如何处理?
    问:在使用图求最短路径时,如果节点之间有多条路径,shortest_route=nx.shortest_path(G,source=start_node,target=end_node,weight='length')会如何处理,会自动选择最短那条吗?#输出图G各节点之间有多少条边edge,并给出其长度Edgesbetween103928and25508583:共2条Edge......
  • 第十一章 图论 Part1
    任务797.所有可能的路径给你一个有n个节点的有向无环图(DAG),请你找出所有从节点0到节点n-1的路径并输出(不要求按特定顺序)graph[i]是一个从节点i可以访问的所有节点的列表(即从节点i到节点graph[i][j]存在一条有向边)。思路题目所给的图的表示是邻接表,题意就是找到......
  • 代码随想录day50 || 图论基础
    图论基础定义图的构造方式1,邻接矩阵矩阵位置array[i][j]=k,i表示节点i,j表示节点j,[i][j]表示i-->j存在一条边,k表示的是边的权重邻接矩阵的优点:表达方式简单,易于理解检查任意两个顶点间是否存在边的操作非常快适合稠密图,在边数接近顶点数平方的图中,邻接矩阵是一种空......
  • 代码随想录 刷题记录-26 图论 (3)最小生成树
    一、prim算法精讲53.寻宝解题思路最小生成树是所有节点的最小连通子图,即:以最小的成本(边的权值)将图中所有节点链接到一起。图中有n个节点,那么一定可以用n-1条边将所有节点连接到一起。那么如何选择这n-1条边就是最小生成树算法的任务所在。例如本题示例中的无......