攒青豆问题 | 青训营活动

72 阅读2分钟

当青训营遇上码上掘金

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

image.png

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

问题分析:

  • 跟接雨水问题很像,我们只关心每个位置自己上方可以留下多少青豆,仅受当前位置两边的高度影响。

举列:当前 i 位置是 5, 左边最大值 10, 右边最大值 20

  • 那么当前位置肯定会停 5 个水,因为超过 10 的话肯定都会从左边溜走了,20再大也无法存放青豆。

  • 所以我们需要 Math.min(数组0到i-1位置最大值,数组i+1到结尾位置最大值)-nums[i] 这个就是对于当前数来说是至少能装的青豆,短的位置决定上限,因此取左右位置最大值的最小值,再减去当前位置的高度就是可以存放的青豆数量。

    • 如果这个结果负数,那么当前数就是 0, 能存 0 个青豆,说明该位置是最高位置,无法存放青豆,必须是一个“凹”的位置才可以存放青豆。

    所以最终是 Math.max(Math.min(数组0到i-1位置最大值,数组i+1到结尾位置最大值)-nums[i],0)

  • 所以只要给每一个位置的都算出这个,让他们加一起就是答案,第一个位置和最后一个除外,因为他们无法存放青豆。

  • 时间复杂度 O (n)

你可能会想用两个辅助数组分别存每一个位置他左边到他的最大值,以及他到他右边的最大值

代码如下:

/**
* 支持 import Java 标准库 (JDK 1.8)
*/
import java.util.*;

/**
* 注意:目前 Java 代码的入口类名称必须为 Main(大小写敏感)
*/
public class Main {
public static void main(String[] args) {
        int[] arr = {5,0,2,1,4,0,1,0,3};
        int res1 = process(arr);
        System.out.println(res1);

    }

    public static int process(int[] arr){
        int[] leftMax = new int[arr.length];
        int[] rightMax = new int[arr.length];
        leftMax[0] = 0;
        leftMax[1] = arr[0];
        rightMax[arr.length-1] = 0;

        // 左边最大
        for (int i = 2; i < arr.length-1; i++) {
            int x = arr[0];
            for (int j = 1 ; j < i;j++){
                x = Math.max(x,arr[j]);
            }
            leftMax[i] = x;
        }
        // 右边最大
        for(int i = 1 ; i < arr.length-1;i++){
            int m = arr[2];
            for (int j = i+1;j< arr.length;j++){
                m = Math.max(m,arr[j]);
            }
            rightMax[i] = m;
        }
        int res = 0;
        // 除首尾位置,计算每一个位置的青豆数
        for(int i = 1 ; i < arr.length-1;i++){
            int tmp = Math.max(Math.min(leftMax[i],rightMax[i])-arr[i],0);
            res += tmp;
        }

        return res;
    }

}