网格图--Day03--网格图DFS--2658. 网格图中鱼的最大数目,1034. 边界着色,1020. 飞地的数量

发布于:2025-09-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

网格图–Day03–网格图DFS–2658. 网格图中鱼的最大数目,1034. 边界着色,1020. 飞地的数量

今天要训练的题目类型是:【网格图DFS】,题单来自@灵艾山茶府

适用于需要计算连通块个数、大小的题目。

部分题目做法不止一种,也可以用 BFS 或并查集解决。

DFS函数中的三步曲:判断,处理,继续DFS

  • 判断:是否越界,是否是需要DFS的格子
  • 处理:根据题意处理格子
  • 继续DFS:DFS四个方向,有时候可能需要收集返回值。

2658. 网格图中鱼的最大数目

思路:

题意转换:0是水,val是陆地宝藏的数值,找到有最大数值宝藏的陆地。

是不是理解起来就简单多了?

遍历每一块陆地,对比它的价值。找到最大价值。

class Solution {
    // 题意转换:0是水,val是陆地宝藏的数值,找到有最大数值宝藏的陆地。

    private final int[][] DIRS = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };

    private int dfs(int[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == 0) {
            return 0;
        }
        int val = grid[i][j];
        // 标记为已访问
        grid[i][j] = 0;

        // 用foreach写,看起来更简洁了
        for (int[] d : DIRS) {
            val += dfs(grid, i + d[0], j + d[1]);
        }
        return val;
    }

    public int findMaxFish(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] != 0) {
                    int val = dfs(grid, i, j);
                    res = Math.max(res, val);
                }
            }
        }
        return res;
    }
}

1034. 边界着色

思路:

题目非常啰嗦且复杂,感觉在做阅读题…………

题意:给指定陆地描边

  • DFS染色的时候,先染在temp矩阵上,dfs完之后再转移回去到grid,不然会影响遍历。复杂很多。
  • 题目说的边界要求:
    • 情况一:上下左右,只要有跟自己的color不同的,就算边界。染色!
    • 情况二:当前格子在边界上,就算边界。染色!
  • DFS下一个节点:要和本节点颜色相同,且没访问过的节点,才能进去。
  • 最后DFS完了,将temp上染色的格子,染回去grid
class Solution {
    // 题意:给指定陆地描边
    private final int[][] DIRS = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };

    // 染色,先染在temp上,dfs完之后再转移回去到grid,不然会影响遍历。复杂很多。
    private void dfs(int[][] grid, int i, int j, int color, boolean[][] visited, int[][] temp) {
        // 标记已访问
        visited[i][j] = true;

        // 情况一:上下左右,只要有跟自己的color不同的,就算边界。染色!
        int cur = grid[i][j];
        if (i - 1 >= 0 && grid[i - 1][j] != cur
                || i + 1 < grid.length && grid[i + 1][j] != cur
                || j - 1 >= 0 && grid[i][j - 1] != cur
                || j + 1 < grid[0].length && grid[i][j + 1] != cur) {
            temp[i][j] = color;
        }
        // 情况二:格子在边界上,就算边界。染色!
        if (i == 0 || j == 0 || i == grid.length - 1 || j == grid[0].length - 1) {
            temp[i][j] = color;
        }

        for (int k = 0; k < DIRS.length; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            // xy越界
            if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) {
                continue;
            }
            // 下一个没访问过的节点,且这个节点要和当前节点是同色的
            if (!visited[x][y] && grid[x][y] == cur) {
                dfs(grid, x, y, color, visited, temp);
            }
        }
    }

    public int[][] colorBorder(int[][] grid, int row, int col, int color) {
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];
        int[][] temp = new int[n][m];
        dfs(grid, row, col, color, visited, temp);

        // dfs完了之后,将temp上染色的格子,染回去grid
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (temp[i][j] != 0) {
                    grid[i][j] = temp[i][j];
                }
            }
        }
        return grid;
    }
}

思路:

不用temp数组的版本。

关键在于:要先判断节点是否被visited过,再判断上下左右是否不同颜色。

这个顺序不能换。因为如果visited过了,颜色可能已经被改了,判断就会有误。

这个顺序是可以不用temp的原因。

public class Solution {
    // 题意:给指定陆地描边
    private final int[][] DIRS = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };

    public int[][] colorBorder(int[][] grid, int row, int col, int color) {
        int m = grid.length;
        int n = grid[0].length;
        boolean[][] visited = new boolean[m][n];
        dfs(grid, row, col, grid[row][col], color, visited);
        return grid;
    }

    private int dfs(int[][] grid, int i, int j, int curColor, int paintColor,
            boolean[][] visited) {
        // 情况一:越界,说明上一个格子是边界
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length) {
            return 1;
        }

        // 已访问过的单元格
        if (visited[i][j]) {
            return 0;
        }

        // 这个顺序不能换,要先判断visited,再判断颜色相同。因为如果visited过了,颜色可能已经被改了,判断就会有误。

        // 情况二:遇到不同颜色的单元格,说明当前单元格是边界
        if (grid[i][j] != curColor) {
            return 1;
        }

        // 标记为已访问
        visited[i][j] = true;

        // 检查四个方向
        int isBorder = 0;
        for (int[] d : DIRS) {
            isBorder += dfs(grid, i + d[0], j + d[1], curColor, paintColor, visited);
        }

        // 如果任何一个方向返回1,说明当前单元格是边界,需要着色
        // 最后才着色
        if (isBorder > 0) {
            grid[i][j] = paintColor;
        }

        return 0;
    }
}

1020. 飞地的数量

思路:

题意:求内陆。

陆地中,必须至少有一个格子要碰到边界,否则这个陆地就是所求陆地(内陆)。

  • DFS搜索图中的每一个岛屿,累加返回每个岛屿的面积。
  • 判断岛屿是否接触边界,如果没有接触边界(内陆),就是所求陆地,累加它的面积到res中。
class Solution {
    // 题意:陆地中,必须至少有一个格子要碰到边界,否则这个陆地就是所求陆地(内陆)。
    private final int[][] DIRS = { { 0, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 } };
    private boolean connect = false;

    private int dfs(int[][] grid, int i, int j) {
        // 越界了,证明递归进来之前,是在边界上的。true
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length) {
            connect = true;
            return 0;
        }
        // 0不一定是边界,这俩条件不能合在一起
        if (grid[i][j] == 0) {
            return 0;
        }

        // 标记已访问
        grid[i][j] = 0;

        // DFS累加,求这个岛的面积
        int area = 1;
        for (int k = 0; k < DIRS.length; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            area += dfs(grid, x, y);
        }
        return area;
    }

    public int numEnclaves(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;

        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 在这里进去每次都是一个独立的岛屿,要把connect重置
                if (grid[i][j] == 1) {
                    connect = false;
                    int area = dfs(grid, i, j);
                    // 如果还是没碰到边界,是所求的岛屿,累加到res
                    if (!connect) {
                        res += area;
                    }
                }
            }
        }
        return res;
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到