leetcode
leetcode 3001 ~ 3050
集水器

集水器

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 147 ms, 内存: 17.6 MB


/*
 * 思路:
 * 1. 从上至下扫描矩阵,找到所有未闭合的区域,并标记这些区域。
 * 2. 通过深度优先搜索(DFS)标记出所有与未闭合区域相连的点。
 * 3. 使用 Java Stream 计算所有未标记的区域的水量,每个未标记的点代表 2 个单位的水量。
 */
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class WaterContainerStream {
    public int calculateWaterVolume(String[] shape) {
        int rows = shape.length;
        int cols = shape[0].length();
        boolean[][] visited = new boolean[rows][cols];
        IntStream.range(0, rows).forEach(i -> {
            IntStream.range(0, cols).forEach(j -> {
                if (shape[i].charAt(j) == '.' && (i == 0 || i == rows - 1 || j == 0 || j == cols - 1)) {
                    dfs(shape, visited, i, j);
                }
            });
        });
        return (int) IntStream.range(0, rows)
                .mapToLong(i -> IntStream.range(0, cols)
                        .filter(j -> !visited[i][j] && shape[i].charAt(j) == '.')
                        .count() * 2)
                .sum();
    }

    private void dfs(String[] shape, boolean[][] visited, int x, int y) {
        int rows = shape.length;
        int cols = shape[0].length();
        if (x < 0 || x >= rows || y < 0 || y >= cols || visited[x][y] || shape[x].charAt(y) != '.') {
            return;
        }
        visited[x][y] = true;
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};
        for (int i = 0; i < 4; i++) {
            dfs(shape, visited, x + dx[i], y + dy[i]);
        }
    }
}

解释

方法:

此题解通过模拟和并查集技术解决集水器问题。每个格子被划分为四个部分(上下左右),用于表示格子中的不同隔板区域。题解策略是通过并查集维护这些部分之间的连通性。首先,题解为每个格子的四个部分分配了唯一的标识符,然后根据格子的隔板类型('l', 'r', 或 '.'),在相应的部分之间建立连接。此外,所有格子的左右边界部分被连接到一个超级汇点,以表示外部空间。然后通过逐行逆向遍历,并查集来判断每个部分是否可以容纳水(即检查该部分是否与外部空间连通)。最后,统计所有可以容纳水的部分,每部分记为2单位水量,然后求出总水量。

时间复杂度:

O(n * m)

空间复杂度:

O(n * m)

代码细节讲解

🦆
在并查集中,如何处理超级汇点与格子边界部分的连接,以及这种连接方式对算法结果的影响是什么?
在题解中,超级汇点被用作一个虚拟的节点,其主要目的是为了模拟格子外部空间的概念。所有的左右边界部分被连接到这个超级汇点。这样的设置主要是为了便捷地处理格子的边界条件,即任何连接到超级汇点的格子部分都认为是与外部空间相连,因此不会积水。这种连接方式简化了边界处理,使得算法只需要关注如何通过内部格子的连接来判断能否积水。结果上,这确保了所有边界格子部分不会误计入积水量,从而提高了算法的准确性和效率。
🦆
题解中提到的逆向遍历格子的原因是什么?直接正向遍历有什么潜在的问题或不足?
题解中选择逆向遍历(从下向上遍历格子)的主要原因是为了有效地处理水的流动性和积存。水通常受到重力作用,从上往下流动,因此从底部开始向上处理可以更自然地模拟这一物理过程。如果采用正向遍历(从上向下),则在处理时需要额外考虑下方格子的状态,这会增加算法的复杂度和处理难度。逆向遍历使得每个格子在处理时,其上方的状态已经确定,可以更直接地判断该格子的水积存情况,从而简化了实现和逻辑判断。
🦆
并查集中的`merge`操作是如何确保隔板类型正确影响到格子部分的连通性的?具体是如何根据隔板的类型`'l'`, `'r'`, `'.'`来决定合并哪些部分?
在题解中,`merge`操作用于将格子的不同部分(上下左右)根据隔板类型连接起来。具体规则如下:当隔板类型为`.`时,表示没有隔板阻碍,因此左、上、下、右四个部分全部相连。当隔板类型为`'l'`时,表示左侧有隔板,因此左侧部分与下方部分连通,右侧部分与上方部分连通。当隔板类型为`'r'`时,表示右侧有隔板,因此左侧部分与上方部分连通,右侧部分与下方部分连通。这种根据隔板类型设定的连接策略确保了隔板的存在正确地影响了格子部分的连通性,从而准确模拟了水的积存情况。

相关问题