「青训营 X 码上掘金」| 攒青豆

82 阅读2分钟

当青训营遇上码上掘金

思路解析

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积) image.png 这道题很像 Leetcode 中的接雨水,下面是之前的自己的想法,参考了windliang的题解

一种最容易想到的就是只考虑单列的情况,求每一列的,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。至于可以装多少青豆,就看左最高墙和右最高墙的较小值了(木桶效应),所以可以分为三种情况:

  • 较矮墙的高度大于当前列墙的高度

    image.png

    很明显,当前列可以装的青豆的大小就是较矮墙的高度,减去当前列的墙的高度即可。

  • 较矮墙的高度小于当前列墙的高度

    image.png

    依旧很明显😁,当前列不能装哪怕一颗青豆。

  • 较矮墙的高度等于当前列墙的高度

    image.png

    依旧很明显😁,和上一种情况一样,当前列不能装哪怕一颗青豆。

所以,只有较矮墙的高度大于当前列墙的高度时,才可以装青豆,装青豆的多少就是它们之间的差值

核心代码

public int trap(int[] height) {
    int sum = 0, len = height.length;
    // 最两端的列不用考虑,因为一定不会有青豆,所以下标从 1 到 len - 2
    for (int i = 1; i < len - 1; i++) {
        int left_max = 0, right_max = 0;
        // 找出左边最高
        for (int j = i - 1; j >= 0; j--) {
            if (left_max < height[j]) {
                left_max = height[j];
            }
        }
        // 找出右边最高
        for (int j = i + 1; j < len; j++) {
            if (right_max < height[j]) {
                right_max = height[j];
            }
        }
        // 找出两端较小的
        int min = Math.min(left_max, right_max);
        // 只有较小的一段大于当前列的高度才会有青豆,其他情况不会有青豆
        if (min > height[i]) {
            sum += min - height[i];
        }
    }
    return sum;
}

具体实现

import java.util.*;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        int[] arr = {5,0,2,1,4,0,1,0,3};
        System.out.println(trap(arr));// 17
    }

    public static int trap(int[] height) {
        int sum = 0, len = height.length;
        // 最两端的列不用考虑,因为一定不会有青豆,所以下标从 1 到 len - 2
        for (int i = 1; i < len - 1; i++) {
            int left_max = 0, right_max = 0;
            // 找出左边最高
            for (int j = i - 1; j >= 0; j--) {
                if (left_max < height[j]) {
                    left_max = height[j];
                }
            }
            // 找出右边最高
            for (int j = i + 1; j < len; j++) {
                if (right_max < height[j]) {
                    right_max = height[j];
                }
            }
            // 找出两端较小的
            int min = Math.min(left_max, right_max);
            // 只有较小的一段大于当前列的高度才会有青豆,其他情况不会有青豆
            if (min > height[i]) {
                sum += min - height[i];
            }
        }
        return sum;
    }
}

显然,这种做法复杂度还是太高了,而且一直循环地找当前列的左极大值和右极大值,可以进行优化,就交给读者你们自己去探索了😋