当青训营遇上码上掘金-主题四攒青豆 题解 & 代码

62 阅读1分钟

当青训营遇上码上掘金

攒青豆

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

攒青豆.png

以下为上图例子的解析:

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

思路

思路1:前缀后缀最大值

经过观察后可以发现

  • 每个位置的青豆数量 = min(左边最高的柱子的高度, 左边最高的柱子的高度) - 该位置的高度 为什么呢?因为槽子的形成一定是由左边和右边最高构成的,所以该位置的豆子数就是由两端最短的柱子和当前的高度所决定。
  • 所以我们可以使用两个数组premx和aftmx分别表示前缀最大值和后缀最大值
  • 最后再遍历一遍数组,计算每个位置的个数,计入总和即可得到答案

main函数代码见:码上掘金code.juejin.cn/pen/7196666…

int beans(vector<int>& height) {
    int ans = 0, n = height.size();
    vector<int> premx(n, height.front()), aftmx(n, height.back());
    for(int i = 1; i < n; i++) premx[i] = max(premx[i-1], height[i]);
    for(int i = n-2; i >= 0; i--) aftmx[i] = max(aftmx[i+1], height[i]);
    for(int i = 0; i < n; i++){
        ans += min(premx[i], aftmx[i]) - height[i];
    }
    return ans;
}

思路2: 双指针

上面这个方法的时间复杂度已经达到O(n)了,但是空间复杂度却还是有点大的,还可以怎样进行优化呢?
考虑一下,如果我们已经知道了一部分前缀的最大值和一部分后缀的最大值,哪些位置是可以计算的?

  • 如果当前位置l的前缀最大值为l_max, 根据思路一来计算,他就等于前后缀最大值中的最小值减去当前高度
  • 此时前缀最大值我们已经知道,那么后缀最大值呢?
  • 考虑后缀r处后缀大值为r_max
  • 如果 发现 l_max < r_max
  • 因为r_max 一定会越来越大,所以l处的min(前缀最大值,后缀最大值)一定等于l_max是吧
  • 所以l处的豆子数量就是 l_max - height[l]
  • 然后我们移动l指针往后
  • 同理如果:l_max >= r_max
  • r处的豆子数量就是 r_max - height[r],移动r指针往前
  • main函数代码见:码上掘金code.juejin.cn/pen/7198098…
int beans(vector<int>& height) {
    int n = height.size(), ans = 0, l = 0, r = n - 1, l_max = 0, r_max = 0;
    while(l <= r){
        l_max = max(l_max, height[l]);
        r_max = max(r_max, height[r]);
        if(l_max < r_max){
            ans += l_max - height[l];
            l++;
        }else{
            ans += r_max - height[r];
            r--;
        }
    }
    return ans;
}