当青训营遇上码上掘金 —— 攒青豆题解

113 阅读2分钟

当青训营遇上码上掘金 —— 攒青豆题解

该文章是 「青训营 X 码上掘金」主题创作活动入营版 开启! - 掘金 (juejin.cn) 活动文章,旨在对主题 4 题解进行详解。

主题 4:攒青豆 现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

本题的思路要点是:如何动态计算每一列柱子可以积攒的青豆数。刚开始看到本题的时候,我尝试使用类似 DFS + 动态规划 的方式不断计算每一段可被用于积攒青豆的空间大小,后来发现这么做使得时间和空间复杂度飙升,而且 BUG 一堆。

但是,在经过 leetcode 老哥的指点后,我发现我忽略了一个重要的做法:我们只需要找到每一列柱子周围两侧最高的柱子,并找到两者之间更低者,用更低者的高度减去该列柱子高度,即可得知该列柱子可积攒的青豆数

Rust 代码如下:

fn save_green_beans(heights: &[u32]) -> u32 {
    let mut result = 0;

    let len = heights.len();
    for (i, v) in heights.iter().enumerate() {
        let mut left_max = 0;
        let mut right_max = 0;

        for j in 0..i {
            if heights[j] > left_max {
                left_max = heights[j];
            }
        }

        for j in i + 1..len {
            if heights[j] > right_max {
                right_max = heights[j];
            }
        }

        let min = std::cmp::min(left_max, right_max);
        if min > *v {
            result += min - v;
        }
    }

    return result;
}

简单来讲,该代码的逻辑大致是:遍历整个高度数组,为每一列柱子计算其左边和右边最高的柱子大小,并计算此两者之间最低的那个柱子(这就像木桶效应一样,最低的木板决定木桶能装多少水),最后,用最低的那个柱子的大小减去该列柱子的大小即可得知该列柱子最大可积攒的青豆数。

当然,此种算法并不是最好的,时间复杂度达到了 O(N²),我们还可以使用更好的方式来计算每一列柱子左右最低的柱子来降低时间复杂度。

最后一提,此题其实就是变种的 接雨水问题,两者并无差别。