算法打卡:第十一章 图论part03

今日收获:孤岛的总面积,沉没孤岛,水流问题,建造最大岛屿

1. 孤岛的总面积

题目链接:101. 孤岛的总面积

思路:只要岛屿中有一个节点是边缘节点,那么这个岛屿就不是孤岛,结果不累加其面积。在深度优先函数中总共有三个地方需要判断:

(1)当前传入节点是否是边缘节点

(2)在下个节点进行深度优先遍历之前,判断这个节点是否是边缘节点

(3)根据深度优先遍历的返回值判断,如果深度优先遍历的过程中出现了边缘节点,则当前岛屿也不是孤岛。

方法:

java 复制代码
import java.util.Scanner;

public class Main{
    static int current;
    static int[][] around={{0,1},{-1,0},{0,-1},{1,0}};
    
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        
        int N=sc.nextInt();
        int M=sc.nextInt();
        
        int[][] grid=new int[N][M];
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                grid[i][j]=sc.nextInt();
            }
        }
        
        boolean[][] visited=new boolean[N][M];
        int result=0;
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (!visited[i][j]&&grid[i][j]==1){
                    current=0;  // 当前岛屿的面积
                    visited[i][j]=true;
                    if (dfs(visited,i,j,grid)){  // 如果是孤岛
                        result+=current;
                    }
                }
            }
        }
        
        System.out.println(result);
        
    }
    
    public static boolean dfs(boolean[][] visited,int x,int y,int[][] grid){
        boolean flag=true;  // 是否为孤岛
        
        current++;  // 计算当前岛屿的面积
        
        if (x==0||y==0||x==grid.length-1||y==grid[0].length-1){
            flag=false;
        }
        
        for (int i=0;i<4;i++){
            int nextX=x+around[i][0];
            int nextY=y+around[i][1];
            
            if (nextX<0||nextY<0||nextX>=grid.length||nextY>=grid[0].length){
                continue;
            }
            
            
            if (!visited[nextX][nextY]&&grid[nextX][nextY]==1){
                if (nextX==0||nextY==0||nextX==grid.length-1||nextY==grid[0].length-1){
                    flag=false;
                }
                
                visited[nextX][nextY]=true;
                if (!dfs(visited,nextX,nextY,grid)){
                    flag=false;
                }
            }
        }
        
        return flag;
    }
}

2. 沉没孤岛

题目链接:102. 沉没孤岛

思路:遍历格子的四条边,将边上相邻的陆地都标记为2。孤岛中的陆地还是原来的1。然后再把1变成0,2变成1,输出格子。(也可以看作是把不同的岛屿做了标记)

方法:

java 复制代码
import java.util.Scanner;

public class Main{
    static int[][] around={{0,1},{-1,0},{0,-1},{1,0}};
    
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        
        int N=sc.nextInt();
        int M=sc.nextInt();
        
        int[][] grid=new int[N][M];
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                grid[i][j]=sc.nextInt();
            }
        }
        
        boolean[][] visited=new boolean[N][M];
        
        // 从格子四周遍历陆地,标记为2
        for (int i=0;i<grid.length;i++){  // 第0列和最后一列
            if (!visited[i][0]&&grid[i][0]==1){
                dfs(visited,i,0,grid);
            }
            
            if (!visited[i][M-1]&&grid[i][M-1]==1){
                dfs(visited,i,M-1,grid);
            }
        }
        
        for (int j=0;j<M;j++){  // 第一行和最后一行
            if (!visited[0][j]&&grid[0][j]==1){
                dfs(visited,0,j,grid);
            }
            
            if (!visited[N-1][j]&&grid[N-1][j]==1){
                dfs(visited,N-1,j,grid);
            }
        }
        
        // 将孤岛变为0,即grid中为1的格子
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (grid[i][j]==1){
                    grid[i][j]=0;
                }
            }
        }
        
        // 将原来的岛屿变为1
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (grid[i][j]==2){
                    grid[i][j]=1;
                }
            }
        }
        
        // 输出
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                System.out.print(grid[i][j]+" ");
            }
            System.out.println(" ");
        }
        
    }
    
    public static void dfs(boolean[][] visited,int x,int y,int[][] grid){
        visited[x][y]=true;
        grid[x][y]=2;
      
        for (int i=0;i<4;i++){
            int nextX=x+around[i][0];
            int nextY=y+around[i][1];
            
            if (nextX<0||nextY<0||nextX>=grid.length||nextY>=grid[0].length){
                continue;
            }
            
            
            if (!visited[nextX][nextY]&&grid[nextX][nextY]==1){
                dfs(visited,nextX,nextY,grid);
            }
        }
    }
}

3. 水流问题

题目链接:103. 水流问题

思路:从两组边界开始逆流而上,判断是否能到达其他格子。最后的结果是从两组边界出发逆流而上都能到达的格子。

方法:

java 复制代码
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;

public class Main{
    static int[][] around={{0,1},{-1,0},{0,-1},{1,0}};
    
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        
        // 接收数据
        int N=sc.nextInt();
        int M=sc.nextInt();
        
        int[][] heights=new int[N][M];
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                heights[i][j]=sc.nextInt();
            }
        }
        
        // 两组边界分别能到达的点
        boolean[][] first=new boolean[N][M];
        boolean[][] second=new boolean[N][M];
        
        // 从两组边界开始逆流而上
        for (int i=0;i<N;i++){
            dfs(heights,first,i,0,Integer.MIN_VALUE);
            dfs(heights,second,i,M-1,Integer.MIN_VALUE);
        }
        
        for (int j=0;j<M;j++){
            dfs(heights,first,0,j,Integer.MIN_VALUE);
            dfs(heights,second,N-1,j,Integer.MIN_VALUE);
        }
        
        // 收集两组边界都能到达的格子
        List<int[]> result=new ArrayList<>();
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (first[i][j]&&second[i][j]){
                    result.add(new int[]{i,j});
                }
            }
        }
        
        // 输出结果
        for (int [] pair:result){
            System.out.println(pair[0]+" "+pair[1]);
        }
    }
    
    public static void dfs(int[][] heights,boolean[][] visited,int x,int y,int pre){
        // 判定合法性
        if (heights[x][y]<pre){
            return;  // 不能逆流而上
        }
        
        // 访问当前节点和相邻节点
        visited[x][y]=true;
        for (int i=0;i<4;i++){
            int nextX=x+around[i][0];
            int nextY=y+around[i][1];
            
            if (nextX<0||nextY<0||nextX>=heights.length||nextY>=heights[0].length){
                continue;
            }
            
            if (!visited[nextX][nextY]){
                dfs(heights,visited,nextX,nextY,heights[x][y]);
            }
        }
    }
}

4. 建造最大岛屿

题目链接:104. 建造最大岛屿

思路:

(1)同属于一个岛屿的格子用相同的数字标记,不同属于一个岛屿的格子标记不同。

(2)用map存储每个岛屿的编号和面积

(3)遍历格子中的0节点,判断其周围格子是否属于某一个岛屿,如果是则将周围岛屿的面积相加,结果遍历中相加结果的最大值

(4)注意:0节点的周围节点所属岛屿面积不能重复相加(周围四个节点其中可能有同属于一个岛屿的),要使用set集合存储相加过面积的岛屿编号

(5)如果格子中没有0节点,结果取所有岛屿面积的最大值(即最多只能变水为陆一次,没有就不变)

方法:

java 复制代码
import java.util.Scanner;
import java.util.HashMap;
import java.util.HashSet;

public class Main{
    static int count=2;  // 岛屿编号
    static int current;  // 岛屿面积
    static int[][] around={{0,1},{-1,0},{0,-1},{1,0}};
    
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        
        int N=sc.nextInt();
        int M=sc.nextInt();
        
        int[][] grid=new int[N][M];
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                grid[i][j]=sc.nextInt();
            }
        }
        
        // 记录岛屿编号和面积
        HashMap<Integer,Integer> map=new HashMap<>();
        boolean[][] visited=new boolean[N][M];
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (!visited[i][j]&&grid[i][j]==1){  // 开始遍历新的岛屿
                    current=0;
                    dfs(visited,i,j,grid);
                    map.put(count,current);
                    count++;
                }
            }
        }
        
        
        int result=0;
        // 遍历格子中的0,判断其周围有没有岛屿,有的话加上面积
        // 注意岛屿编号不能重复访问
        HashSet<Integer> set=new HashSet<>();
        for (int i=0;i<N;i++){
            for (int j=0;j<M;j++){
                if (grid[i][j]==0){
                    int area=1;
                    for (int k=0;k<4;k++){
                        int nextX=i+around[k][0];
                        int nextY=j+around[k][1];
                        
                        if (nextX<0||nextY<0||nextX>=grid.length||nextY>=grid[0].length){
                            continue;
                        }
                        
                        if (map.containsKey(grid[nextX][nextY])&&!set.contains(grid[nextX][nextY])){
                            set.add(grid[nextX][nextY]);
                            area+=map.get(grid[nextX][nextY]);
                        }
                    }
                    result=Math.max(result,area);
                    set.clear();
                }
            }
        }
        
        // 如果格子中没有0,取岛屿的最大面积
        for (int key:map.keySet()){
            result=Math.max(result,map.get(key));
        }
        
        System.out.println(result);
    }
    
    public static void dfs(boolean[][] visited,int x,int y,int[][] grid){
        visited[x][y]=true;
        grid[x][y]=count;
        current++;
        
        for (int i=0;i<4;i++){
            int nextX=x+around[i][0];
            int nextY=y+around[i][1];
            
            if (nextX<0||nextY<0||nextX>=grid.length||nextY>=grid[0].length){
                continue;
            }
            
            if (!visited[nextX][nextY]&&grid[nextX][nextY]==1){
                dfs(visited,nextX,nextY,grid);
            }
        }
    }
}
相关推荐
CoovallyAIHub2 分钟前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
Java中文社群11 分钟前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心20 分钟前
从零开始学Flink:数据源
java·大数据·后端·flink
间彧25 分钟前
Spring Boot项目中如何自定义线程池
java
间彧1 小时前
Java线程池详解与实战指南
java
用户298698530141 小时前
Java 使用 Spire.PDF 将PDF文档转换为Word格式
java·后端
NAGNIP1 小时前
Serverless 架构下的大模型框架落地实践
算法·架构
moonlifesudo1 小时前
半开区间和开区间的两个二分模版
算法
渣哥1 小时前
ConcurrentHashMap 1.7 vs 1.8:分段锁到 CAS+红黑树的演进与性能差异
java
moonlifesudo1 小时前
300:最长递增子序列
算法