「青训营 X 码上掘金」攒青豆

82 阅读3分钟

题目要求

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

解析

攒青豆的问题我们可以首先通过手动模拟攒青豆的过程来尝试理解具体如何进行攒青豆,首先遍历攒青豆的立柱高度,可以发现遍历到某些柱子的时候,会由于和之前的某个柱子形成凹形的坑,这些凹形的坑就可以用于攒青豆。了解了上述的过程,这道题目实际上可以用单调栈来做。单调栈就是比普通的栈多一个性质,即维护一个栈内元素单调。

例如当前某个单调递增的栈的元素是从栈底到栈顶分别为:[1,2,3,4,7],如果要入栈的元素为6,那么要先把栈顶元素pop出去,直到满足加入元素6后仍然单调递增,即先变成[1,2,3,4],在压入6,变成[1,2,3,4,6]。

我们以[5,0,2,1,4,0,1,0,3]为例,遍历一下该攒青豆的过程:

首先我们定义一个单调递减栈。

整个过程中首先是5入栈,此时的栈中元素为[5]。

之后是0入栈,此时栈中元素为[5,0]。

然后2入栈,因为2大于0,因此此时0出栈,2入栈。此时栈中元素为[5,2]。此次进出栈操作攒下的青豆为0与5和2高度差的最小值×\times左右边的位置差=2×\times1=2。

然后1入栈,因为1小于2,此时栈中元素为[5,2,1]。

然后4入栈,因为4大于1,因此1出栈,2同样小于4,2出栈,此时栈中元素为[5,4]。此次进出栈操作攒下的青豆为1与左右两边高度最小差值×\times1+2与左右两边高度最小差值×\times3=1+6=7。

然后0入栈,此时栈中元素为[5,4,0]。

然后1入栈,因为1大于0,所以0出栈,此时栈中元素为[5,4,1],此时进出栈操作攒下的青豆数为0与左右两边高度差最小值×\times1=1.

然后0入栈,此时栈中元素为[5,4,1,0]。

最后3入栈,因为3比0大,所以0出栈,又因为3比1大,所以1出栈,此次进出栈操作攒下的青豆为,0与左右两边高度差最小值1+1与左右两边高度差最小值×\times3=1+2×\times3=7。

最后攒下的总青豆数=2+7+1+7=17。在计算所攒青豆数的过程中,因为每个柱体每次只出入栈一次,因此,时间复杂度为O(n)。

具体代码如下: