攒青豆 | 当青训营遇上码上掘金

52 阅读2分钟

当青训营遇上码上掘金,此次我选择的主题为“攒青豆”,问题描述为“现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)”。接下来我将描述我的思路和想法。

image.png 总体思路是逐个计算每一个柱子所能接住的青豆,最后进行相加得出青豆总和。这里的想法其实就是通过解决局部的问题进而扩大到整体。
每一个柱子所能接住的青豆与左方最高的柱子高度maxleft和右方最高的柱子高度maxright相关。该柱子所能接住的青豆数量等于maxleft和maxright的最小值减去该柱子本身的高度。分析如下:
假设任一柱子为A,并且A左方的最高柱子L高于右方的最高柱子R,那么L处的青豆会往右边散开,把A处的青豆逐步堆积升高,并且也开始往R方向散开,直到升高到和R一样高时才会停止。
因此我们需要两个列表maxleft和maxright来存储每一个柱子左边柱子最高高度和右边柱子最高高度。第一个柱子的青豆数量和最后一个柱子的青豆数量只会是0。此时输入为柱子高度的列表height,输出为数字qingdou_sum。

  var maxleft = []
  var maxright = []
  var height_len = height.length
  var qingdou_sum = 0
  maxleft[0] = 0
  maxright[height_len-1] = 0

从左往右开始遍历,从第二个柱子开始,计算每个柱子左边的最高柱子高度。每一个柱子左边的最高柱子高度等于前一个柱子的左边最高柱子高度和前一个柱子的高度之间的最大值。

  for(let i=1;i<height_len;i++)
  {
    maxleft[i] =Math.max(maxleft[i-1],height[i-1]) 
  }

同理从右往左遍历,从倒数第二个柱子开始,计算每个柱子右边的最高柱子高度。每一个柱子右边的最高柱子高度等于后一个柱子的右边最高柱子高度和后一个柱子的高度之间的最大值。

    for(let i=height_len-2;i>=0;i--)
  {
    maxright[i] =Math.max(maxright[i+1],height[i+1]) 
  }

最后逐个遍历每一个柱子,该柱子的的青豆数量等于MAX( maxleft[i], height[i+1])-height[i],(如果计算出来小于等于0,则该柱子的青豆数量就是0,这说明该柱子高度比某一侧的最高柱子高度还要高,无法接住青豆)

  for(let i=1;i<height_len-1;i++){
    let r = Math.min(maxright[i],maxleft[i]) - height[i]
    if(r>0){
    qingdou_sum = qingdou_sum + r
    }
  }