代码随想录算法训练营第五十五天|101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿

news2024/11/26 4:38:57

101.孤岛的总面积

在这里插入图片描述

题目链接:101.孤岛的总面积沉没孤岛
文档讲解:代码随想录
状态:不会

思路:
步骤1:将边界上的陆地变为海洋
步骤2:计算孤岛的总面积

题解:

public class Main {
    // 保存四个方向
    static int[][] dir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};

    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();
            }
        }
        int sum = 0;
        // 将边界上的陆地变为海洋
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 1 && (i == 0 || i == n - 1 || j == 0 || j == m - 1)) {
                    dfs(grid, i, j);
                }
            }
        }
        // 计算孤岛的总面积
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 1) {
                    sum += dfs(grid, i, j);
                }
            }
        }
        // 输出孤岛的总面积
        System.out.println(sum);
    }

    /**
     * 深度优先搜索遍历陆地,将其变为海洋,并计算陆地的面积
     * @param grid 网格
     * @param x 当前的行坐标
     * @param y 当前的列坐标
     * @return 陆地的面积
     */
    public static int dfs(int[][] grid, int x, int y) {
        // 如果超出网格边界或者当前位置是海洋,则返回0
        if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == 0) {
            return 0;
        }
        // 将当前位置的陆地变为海洋
        grid[x][y] = 0;
        int sum = 1;
        // 向四个方向遍历
        for (int i = 0; i < 4; i++) {
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];
            sum += dfs(grid, nextX, nextY);
        }
        return sum;
    }
}

102.沉没孤岛

在这里插入图片描述

题目链接:102.沉没孤岛
文档讲解:代码随想录
状态:还行

思路:在上题的基础上额外标记下边界上的陆地

题解:

public class Main {
    // 保存四个方向
    static int[][] dir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};

    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();
            }
        }
        // 标记边界上的陆地
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 1 && (i == 0 || i == n - 1 || j == 0 || j == m - 1)) {
                    dfsEdge(grid, i, j);
                }
            }
        }

        // 沉没孤岛
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 1) {
                    dfs(grid, i, j);
                }
            }
        }

        // 恢复标记为-1的边界陆地为原始状态
        for (int[] ints : grid) {
            for (int k : ints) {
                if (k == -1) {
                    k = 1;
                }
                System.out.print(k + " ");
            }
            System.out.println();
        }
    }

    /**
     * 深度优先搜索标记边界上的陆地
     * @param grid 网格
     * @param x 当前的行坐标
     * @param y 当前的列坐标
     */
    public static void dfsEdge(int[][] grid, int x, int y) {
        // 如果超出网格边界或者当前位置不是陆地,则返回
        if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] != 1) {
            return;
        }
        // 将边界上的陆地标记为-1
        grid[x][y] = -1;
        // 向四个方向遍历
        for (int i = 0; i < 4; i++) {
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];
            dfsEdge(grid, nextX, nextY);
        }
    }

    /**
     * 深度优先搜索沉没孤岛
     * @param grid 网格
     * @param x 当前的行坐标
     * @param y 当前的列坐标
     */
    public static void dfs(int[][] grid, int x, int y) {
        // 如果超出网格边界或者当前位置不是陆地,则返回
        if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] != 1) {
            return;
        }
        // 将当前位置的陆地沉没
        grid[x][y] = 0;
        // 向四个方向遍历
        for (int i = 0; i < 4; i++) {
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];
            dfs(grid, nextX, nextY);
        }
    }
}

103.水流问题

在这里插入图片描述在这里插入图片描述

题目链接:103.水流问题
文档讲解:代码随想录
状态:没做出来

思路:
雨水的流动方向是从高到低,每个单元格上的雨水只能流到高度小于等于当前单元格的相邻单元格。从一个单元格开始,通过搜索的方法模拟雨水的流动,则可以判断雨水是否可以从该单元格流向边界。

如果直接以每个单元格作为起点模拟雨水的流动,则会重复遍历每个单元格,导致时间复杂度过高。为了降低时间复杂度,可以从矩阵的边界开始反向搜索寻找雨水流向边界的单元格,反向搜索时,每次只能移动到高度相同或更大的单元格。

题解:

public class Main {
    // 方向数组,包括右、下、左、上四个方向
    static int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    /**
     * 找到能够流向两个边界的坐标
     * @param heights 给定的高度矩阵
     * @return 能够流向两个边界的坐标列表
     */
    public static List<int[]> WaterFlow(int[][] heights) {
        List<int[]> result = new ArrayList<>();
        if (heights == null || heights.length == 0 || heights[0].length == 0) {
            return result;
        }

        int m = heights.length;     // 矩阵的行数
        int n = heights[0].length;  // 矩阵的列数

        boolean[][] firstBoard = new boolean[m][n];  // 记录能流向第一个边界的坐标
        boolean[][] secondBoard = new boolean[m][n]; // 记录能流向第二个边界的坐标

        // 遍历第一列和第一行,找到能流向第一个边界的坐标
        for (int i = 0; i < m; i++) {
            dfs(heights, firstBoard, i, 0);
        }
        for (int j = 1; j < n; j++) {
            dfs(heights, firstBoard, 0, j);
        }

        // 遍历最后一列和最后一行,找到能流向第二个边界的坐标
        for (int i = 0; i < m; i++) {
            dfs(heights, secondBoard, i, n - 1);
        }
        for (int j = 0; j < n - 1; j++) {
            dfs(heights, secondBoard, m - 1, j);
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                dfs(heights,firstBoard,i,j);
                dfs(heights,secondBoard,i,j);
            }
        }

        // 找到能流向两个边界的坐标
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (firstBoard[i][j] && secondBoard[i][j]) {
                    result.add(new int[]{i, j});
                }
            }
        }

        return result;
    }

    /**
     * 深度优先搜索DFS
     * @param heights 给定的高度矩阵
     * @param visited 记录访问过的坐标
     * @param x 当前点的行坐标
     * @param y 当前点的列坐标
     */
    private static void dfs(int[][] heights, boolean[][] visited, int x, int y) {
        visited[x][y] = true;   // 标记当前点已经访问过
        int m = heights.length; // 矩阵的行数
        int n = heights[0].length;  // 矩阵的列数

        // 遍历四个方向
        for (int[] dir : directions) {
            int newX = x + dir[0];   // 新点的行坐标
            int newY = y + dir[1];   // 新点的列坐标

            // 如果新点在矩阵范围内,并且新点的高度不小于当前点的高度,且新点未被访问过,则继续递归
            if (newX >= 0 && newX < m && newY >= 0 && newY < n && !visited[newX][newY] && heights[newX][newY] >= heights[x][y]) {
                dfs(heights, visited, newX, newY);
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 矩阵的行数
        int m = scanner.nextInt(); // 矩阵的列数
        int[][] grid = new int[n][m]; // 创建高度矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                grid[i][j] = scanner.nextInt(); // 输入矩阵元素
            }
        }

        // 调用函数找到能够流第一边界和第二边界的点
        List<int[]> result = WaterFlow(grid);

        // 输出结果
        for (int[] point : result) {
            System.out.println(point[0] + " " + point[1]);
        }
    }
}

104.建造最大岛屿

在这里插入图片描述
在这里插入图片描述

题目链接:104.建造最大岛屿
文档讲解:代码随想录
状态:只想到暴力

思路:
暴力的思想:本题的一个暴力想法,应该是遍历地图尝试 将每一个 0 改成1,然后去搜索地图中的最大的岛屿面积。

其实每次深搜遍历计算最大岛屿面积,我们都做了很多重复的工作。

只要用一次深搜把每个岛屿的面积记录下来就好。

第一步:一次遍历地图,得出各个岛屿的面积的同时做编号记录。可以使用map记录,key为岛屿编号,value为岛屿面积。(遍历过后的陆地标记为编号数字,map中通过编号数字对应岛屿面积)

第二步:再遍历地图,遍历0的方格(因为要将0变成1),并统计该1(由0变成的1)周边岛屿面积,将其相邻面积相加在一起,遍历所有 0 之后,就可以得出 选一个0变成1 之后的最大面积。对每个0方格遍历时要考虑相邻坐标越界,相邻格子的岛屿是否已经计算过,最后才是累加相邻岛屿的面积,更新最大岛屿面积。

题解:

public class Main {
    // 定义四个方向的移动
    static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};

    // 深度优先搜索函数,用于标记岛屿并计算其面积
    public static int dfs(int[][] grid, int x, int y, int mark) {
        int n = grid.length;
        int m = grid[0].length;
        // 如果坐标越界或当前格子不是陆地(不是1),则返回0
        if (x < 0 || x >= n || y < 0 || y >= m || grid[x][y] != 1) {
            return 0;
        }
        // 将当前陆地格子标记为当前岛屿的标记
        grid[x][y] = mark;
        int sum = 1; // 记录当前岛屿的面积
        // 遍历四个方向,继续深度优先搜索
        for (int i = 0; i < 4; i++) {
            int newX = x + dir[i][0];
            int newY = y + dir[i][1];
            sum += dfs(grid, newX, newY, mark); // 累加相邻陆地的面积
        }
        return sum;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 输入网格的行数
        int m = scanner.nextInt(); // 输入网格的列数
        int[][] grid = new int[n][m]; // 初始化网格
        // 读取网格数据
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                grid[i][j] = scanner.nextInt();
            }
        }

        HashMap<Integer, Integer> map = new HashMap<>(); // 存储每个岛屿的标记及其面积
        boolean isAllGrid = true; // 标记是否整个地图都是陆地

        int mark = 2; // 从2开始标记岛屿,因为1已经表示陆地
        // 遍历网格,对每个未标记的陆地进行DFS
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) {
                    isAllGrid = false; // 如果有海洋格子,标记不是全陆地
                }
                if (grid[i][j] == 1) {
                    int area = dfs(grid, i, j, mark); // 对新的岛屿进行DFS并计算面积
                    map.put(mark++, area); // 记录岛屿的标记和面积
                }
            }
        }

        // 如果整个地图都是陆地,直接返回面积
        if (isAllGrid) {
            System.out.println(n * m);
            return;
        }

        int maxArea = 0; // 记录最大岛屿面积
        // 遍历网格,尝试将每个海洋格子变为陆地,计算可能的最大岛屿面积
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) {
                    int sum = 1; // 初始面积为1,因为将海洋格子变为陆地
                    // 在计算相邻岛屿面积时,需要避免重复计算,因此使用一个哈希集合 (visitedMarks) 来记录已经计算过的岛屿编号。
                    HashSet<Integer> visitedMarks = new HashSet<>(); // 存储已经访问过的岛屿标记

                    // 遍历四个方向,计算相邻岛屿的总面积
                    for (int k = 0; k < 4; k++) {
                        int newX = i + dir[k][0];
                        int newY = j + dir[k][1];
                        // 如果相邻坐标越界,跳过
                        if (newX < 0 || newX >= n || newY < 0 || newY >= m) continue;
                        int markValue = grid[newX][newY];
                        // 如果相邻格子的岛屿已经计算过,跳过
                        if (visitedMarks.contains(markValue)) continue;
                        Integer s = map.get(markValue); // 获取相邻岛屿的面积
                        if (s != null) {
                            sum += s; // 累加相邻岛屿的面积
                            visitedMarks.add(markValue); // 标记该岛屿已经访问
                        }
                    }
                    maxArea = Math.max(maxArea, sum); // 更新最大岛屿面积
                }
            }
        }
        System.out.println(maxArea); // 输出最大岛屿面积
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1930098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【UE5.1】NPC人工智能——02 NPC移动到指定位置

效果 步骤 1. 新建一个蓝图&#xff0c;父类选择“AI控制器” 这里命名为“BP_NPC_AIController”&#xff0c;表示专门用于控制NPC的AI控制器 2. 找到我们之前创建的所有NPC的父类“BP_NPC” 打开“BP_NPC”&#xff0c;在类默认值中&#xff0c;将“AI控制器类”一项设置为“…

动手学深度学习——3.多层感知机

1.线性模型 线性模型可能出错 例如&#xff0c;线性意味着单调假设&#xff1a; 任何特征的增大都会导致模型输出的增大&#xff08;如果对应的权重为正&#xff09;&#xff0c; 或者导致模型输出的减小&#xff08;如果对应的权重为负&#xff09;。 有时这是有道理的。 例…

R绘制Venn图及其变换

我自己在用R做各种分析时有不少需要反复用到的基础功能&#xff0c;比如一些简单的统计呀&#xff0c;画一些简单的图等等&#xff0c;虽说具体实现的代码也不麻烦&#xff0c;但还是不太想每次用的时候去找之前的代码。 索性将常用的各种函数整成了一个包&#xff1a;pcutils…

前端JS特效第34集:jQuery俩张图片局部放大预览插件

jQuery俩张图片局部放大预览插件&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下(全部代码在文章末尾)&#xff1a; <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Co…

数据结构与算法02迭代|递归

目录 一、迭代(iteration) 1、for循环 2、while循环 二、递归&#xff08;recursion&#xff09; 1、普通递归 2、尾递归 3、递归树 三、对比 简介&#xff1a;在算法中&#xff0c;重复执行某个任务是常见的&#xff0c;它与复杂度息息相关&#xff0c;在程序中实现重…

MySQL MVCC原理

全称Multi-Version Concurrency Control&#xff0c;即多版本并发控制&#xff0c;主要是为了提高数据库的并发性能。 1、版本链 对于使用InnoDB存储引擎的表来说&#xff0c;它的聚簇索引记录中都包含两个必要的隐藏列&#xff1a; 1、trx_id&#xff1a;每次一个事务对某条…

connect by prior 递归查询

connect by prior 以公司组织架构举例&#xff0c;共四个层级&#xff0c;总公司&#xff0c;分公司&#xff0c;中心支公司&#xff0c;支公司 总公司level_code为1 下一层级的parent_id为上一层级的id&#xff0c;建立关联关系 SELECT id, name, LEVEL FROM org_info a STA…

区块链学习05-web3中solidity和move语言

Solidity 和 Move 语言的比较&#xff1a;Web3 开发中的两种选择 Solidity 和 Move 都是用于开发区块链平台智能合约的编程语言。它们具有一些相似之处&#xff0c;但也存在一些关键差异。 相似之处: Solidity 和 Move 都是图灵完备语言&#xff0c;这意味着它们可以表达计算…

提高引流精准性的策略

1、定位清晰&#xff1a;明确你的目标用户是谁&#xff0c;了解他们的需求和兴趣&#xff0c;定制内容和策略以吸引他们。 2、价值输出&#xff1a;提供有价值的内容或服务&#xff0c;让用户觉得添加你的微信是有益的&#xff0c;比如独家资讯、优惠券、专业咨询等。 3、筛选…

vs2019 QT无法打开源文件QModbusTcpClient

vs2019无法打开源文件QModbusTcpClient 如果配置的msvc2019,则查找到Include目录 然后包含&#xff1a; #include <QtSerialBus/qmodbustcpclient.h>

PostgreSQl 物化视图

物化视图&#xff08;Materialized View&#xff09;是 PostgreSQL 提供的一个扩展功能&#xff0c;它是介于视图和表之间的一种对象。 物化视图和视图的最大区别是它不仅存储定义中的查询语句&#xff0c;而且可以像表一样存储数据。物化视图和表的最大区别是它不支持 INSERT…

【设计模式】【创建型模式】【02工厂模式】

系列文章 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 系…

redis原理之底层数据结构(三)-quicklist

1.绪论 前面讲过的ziplist在查找元素的时候是o(n)的复杂度&#xff0c;如果ziplist长度太长&#xff0c;会导致查找元素很缓慢&#xff0c;而ziplist是拥有内存连续的优势&#xff0c;为了保留ziplist内存连续的优势&#xff0c;但是又不能保留太长的长度&#xff0c;我们出现…

MQ基础1

对应B站视频&#xff1a; MQ入门-01.MQ课程介绍_哔哩哔哩_bilibili 微服务一旦拆分&#xff0c;必然涉及到服务之间的相互调用&#xff0c;目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中&#xff0c;调用者发起请求后需要等待服务提供者执行业务返回结果后…

【Linux杂货铺】期末总结篇3:用户账户管理命令 | 组账户管理命令

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux杂货铺、Linux实践室 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 第五章5.1 ⛳️Linux 账户5.2 ⛳️用户配置文件和目录&#xff08;未完待续&#xff09;5.2.1 …

java面向对象进阶篇--static

一、前言 java进阶篇已经开始了&#xff0c;先从面向对象开始&#xff0c;由于时间原因今天就只更新了static部分&#xff0c;内容上特别详细&#xff0c;一些特别的注意事项也在反复的提醒大家。 温馨提示一下&#xff0c;往后的java篇会越来越难&#xff0c;希望大家能够坚…

推荐5个实用的可视化工具

面对海量的数据&#xff0c;我们应该如何高效地提取其价值&#xff0c;让复杂的信息一目了然&#xff1f;这正是可视化工具大显身手的舞台。今天&#xff0c;我就来分享几款非常好用的数据可视化工具&#xff0c;它们不仅能够帮助你轻松驾驭数据&#xff0c;还能让你的工作汇报…

vite配置环境变量和使用,配置正确后import.meta.env.VITE_APP_BASE_URL编译报错的解决方法

一、配置&#xff1a; 1.新增四个环境文件 .env.development .env.test .env.production .env.pre 内容为不同环境的不同参数变量必须以VITE_APP开头&#xff0c;如&#xff1a; #接口地址 VITE_APP_BASE_URL"&#xffe5;&#xffe5;&#xffe5;&#xffe5;&#xff…

算法 —— 快速幂

目录 P1045 [NOIP2003 普及组] 麦森数 P1226 【模板】快速幂 原理I 原理II P1226 代码解析 P1045 代码解析 P1045 [NOIP2003 普及组] 麦森数 本题来自洛谷&#xff1a;P1045 [NOIP2003 普及组] 麦森数&#xff0c;根据题意&#xff0c;我们可以看到本题需要计算最少2的1…

【Linux】权限的管理和Linux上的一些工具

文章目录 权限管理chgrpchownumaskfile指令sudo指令 目录权限粘滞位Linux中的工具1.软件包管理器yum2.rzsz Linux开发工具vim 总结 权限管理 chgrp 功能&#xff1a;修改文件或目录的所属组 格式&#xff1a;chgrp [参数] 用户组名 文件名 常用选项&#xff1a;-R 递归修改文…