青训营-主题四攒青豆-简单易懂解法

212 阅读2分钟

当青训营遇上码上掘金

主题 4:攒青豆

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

示例 1: 725ef710a59944d49d0315bece7a1ac1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp 以下为上图例子的解析:

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

示例 2:

接雨水.png 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6

思路

想不出动态规划咋办,看不懂单调栈咋办,双指针也看得云里雾里。这里我们可以用面积减法来解。总面积=青豆面积+柱子面积+空气面积。

以示例2为例:

攒青豆图1.png

  • 青豆的面积=总的面积-柱子面积-空气面积。
  • 总的面积=最高柱子高度*柱子个数。
  • 柱子面积=累加(柱子高度)。
  • 空气面积=S1+S2+S3+S4+S5+S6S_1+S_2+S_3+S_4+S_5+S_6

如何计算上方空气小块的面积?以S2S_2为例:

计算小块面积2.png

我们可以往右遍历HeightHeight数组,jj为其下标,每发现柱子高度高于之前记录的,就可以认为发现了一个空气小块,prej用记录上一个高度的下标pre_j用记录上一个高度的下标

S2=高的长度*底的长度=(maxHheight[prej])(jprej)(maxH-height[pre_j])*(j-pre_j)

从左往右计算左边的空气块,再从右边往左计算右边的空气块。

算法流程

  • 1.计算最高柱子高度
  • 2.计算总体面积。
  • 3.计算柱子总面积。
  • 4.从左往右直到最高柱子位置计算空气小块面积。
  • 5.从右往左直到最高柱子位置计算空气小块面积。
  • 6.计算青豆面积。青豆面积=总体面积-柱子面积-空气面积。

复杂度分析

  • 时间复杂度O(n):O(n)。n是height数组的长度,需遍历两次heightheight数组。
  • 空间复杂度O(1):O(1)。只需常量额外内存。

C++代码

#include<iostream>
#include <vector>
using namespace std;
int main() {
  int a;
  vector<int> height;
  while(cin>>a){
    height.push_back(a);
    if(getchar()=='\n') break;
  }
  int len=height.size();
  int maxh=0,ans=0;
  for(int i =0;i<len;i++){
      if(height[i]>height[maxh]) maxh=i;
      ans-=height[i];
  }
  ans+=len*height[maxh];
  for(int j=0,pos=0;j<=maxh;j++){
      if(height[j]>height[pos]){
          ans-=(height[maxh]-height[pos])*(j-pos);
          pos=j;
      }
  }
  for(int j=len-1,pos=len-1;j>=maxh;j--){
      if(height[j]>height[pos]) {
          ans-=(height[maxh]-height[pos])*(pos-j);
          pos=j;
      }
  }
  cout<<ans<<endl;
  return 0;
}