「青训营 X 码上掘金」主题 4:攒青豆

57 阅读3分钟

当青训营遇上码上掘金

        现有 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 个单位的青豆。

*解题思路:

       由图可得,青豆是在左右两端柱子之间的空隙攒下来的,不同的柱子高度能积攒不同数量的青豆,在由两端柱子隔绝的每一堆青豆中,我们发现:两端的柱子分别是这一堆柱子中最高的和次高的柱子。

       根据木桶效应可知:每个木桶能装的水的高度取决于最短的木板,即以最短的木板高度为基准。所以在攒青豆中,我们可以很容易地想到,一排柱子的左右两端,需要以较短的一端为基准高度。

       那么这个问题可以转化为:如何在一排柱子中找到一堆堆的青豆,即如何在一排柱子中找到几组特殊的“端点柱子”。

       所以我们需要从最左、最右两端的柱子同时开始,比较它们的长短,注意,这里首先有个坑:如果较短的一端的柱子长度为0,那么它无法攒下青豆,以它为基准就没有任何意义,故我们需要将长度为0的某一端柱子“剔除”。

         在示例中我们可以看到,最右端的柱子比最左端的柱子短,那么我们首先以最右端柱子往左看,一直找到一个高度为4的柱子比最右端柱子高(或等高),于是我们找到了第一个青豆堆,计算其青豆数量,再将较高的柱子标记为最右端;然后我们继续找下一个青豆堆,一直找到一个高度为5的柱子比最右端柱子高(或等高),于是我们找到了第二个青豆堆,计算其青豆数量。再将较高的柱子标记为最右端,由于此时最左端和最右端都是同一个柱子,表示青豆已经攒完了,此题得解~

       至于每一堆的青豆数量要怎么计算,要知道我们在找青豆堆的时候已经找到了其中一端的柱子,那么中间的柱子若有高度,则该柱子的高度为阻碍青豆的积攒数,将中间柱子的高度累加,直到找到该青豆堆的另一端柱子,两端柱子以较短的一端为青豆堆的高度,青豆堆的宽度为两端柱子的距离,相乘后再减去中间柱子的高度和,即为该青豆堆的数量。

本人的代码:

function gatherBeans(arr) {  let sum = 0;  let left = 0;  let right = arr.length - 1;    while(left < right) {    if(arr[left] > arr[right]) {      if(arr[right] == 0) {        right--;        continue;      }      for(let i = right - 1; i >= left; i--) {        if(arr[i] < arr[right]) {          sum -= arr[i];        } else {          sum += arr[right] * (right - i - 1);          right = i;          break;        }      }          } else{      if(arr[left] == 0) {          left++;          continue;      }      for(let i = left + 1; i <= right; i++) {        if(arr[i] < arr[left]) {          sum -= arr[i];        } else {          sum += arr[left] * (i - left - 1);          left = i;          break;        }      }    }  }  return sum;}