攒青豆

110 阅读2分钟

当青训营遇上码上掘金。

题目描述

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(详见活动主题4

bean.webp

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

解题思路

该题目与 leetcode 接雨水是很相似的,解法也很相似。从图中我们可以想到有两种不同的计算方式:一是按列计算,每次只计算宽度为 1 的一列区域;二是按行(或者说块)计算,每次计算两个柱子之间的一个矩形区域。

如果采用按列计算的方法,可以看出每一列青豆的高度取决于该列左侧最高的柱子和右侧最高的柱子中较矮的那个柱子的高度,即h青豆=min(max(h),max(h))h_{青豆}=min(max(h_{柱_左}), max(h_{柱_右}))。因此,该方法的关键就是要确定每一列的左侧和右侧最高的柱子的高度。该方法可以使用双指针或者动态规划算法来实现。

如果采用按行(块)计算的方法,可以看出每个矩形区域青豆的宽取决于其左右两侧柱子的距离,高取决于其左右两侧柱子中较矮的那个柱子的高度与中间最高的柱子的高度之差,即w青豆=distance(,)w_{青豆}=distance(柱_左,柱_右)h青豆=min(h,h)max(h中间)h_{青豆}=min(h_{柱_左},h_{柱_右})-max(h_{柱_{中间}})S青豆=w青豆×h青豆S_{青豆}=w_{青豆}\times h_{青豆}。该方法可以使用单调栈来实现。

解题过程

按列计算的方法较为简单,这里就不做过多的介绍了,重点来讲述一下基于单调栈来实现的按行(块)计算的方法。

栈中存储的元素是输入 height 数组的下标。

入栈出栈流程:

 for i, h in height: // i为下标, h为高度
     if stack为空:
         stack.push(i)
     else if stack不为空 and height[stack.peek()]>=h: // peek操作获取栈顶元素但不弹出
         stack.push(i)
     else:
         for stack不为空 and height[stack.peek()]<h:
             top = stack.pop()
             if stack不为空:
                 left = stack.peek()
                 bean_width = i-left-1
                 bean_height = min(height[left]-h)-height[top]
                 s = bean_width*bean_height
         stack.push(i)

具体代码见码上掘金