当青训营遇上码上掘金:接豆子

122 阅读2分钟

当青训营遇上码上掘金

题目介绍

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

题目分析

这道题与知名难题接雨水是一类题目,可以使用计算面积的方法来解决。

如图所示,豆子的面积 = 最高处柱子围成的长方形面积 - 所有柱子的面积 - 豆子上方的空白

已知,最高处柱子围成的长方形面积 = max(height) * len(leight)所有柱子的面积 = sun(height),只要求出豆子上方的空白之处的面积就可以解出豆子的面积。

然而,很难用常规的方法求解上方的面积。但是,我们发现,分别计算最高处左右的空白面积很简单。

1673961448327.png

以本题为例。由于最高处在最左端,因此只要考虑最高处右端的面积即可,即红线、蓝线与黑线围成的面积。 用代码表示为

# s为所求的面积
s = 0
h_right = 0
for i in range(len(height)-1,h_max_index - 1,-1):
 # 从右往左
 h_right = max(h_right,height[i]) 
 s += h_max - h_right

从左往右也是一样的

s = 0
h_left = 0
for i in range(h_max_index + 1):
 # 从右往左
 h_left = max(h_left,height[i]) 
 s += h_max - h_left

将上述步骤结合,就有了第一版代码

s1 = max(height) * len(leight)
s2 = sun(height)
h_max_index = height.index(max(height))
s3 = 0
h_right = 0
for i in range(len(height)-1,h_max_index - 1,-1):
 # 从右往左
 h_right = max(h_right,height[i]) 
 s4 += h_max - h_right
s4 = 0
h_left = 0
for i in range(h_max_index + 1):
 # 从右往左
 h_left = max(h_left,height[i]) 
 s4 += h_max - h_left
s = s1-s2-s3-s4

使用上述代码过于复杂,我们尝试对代码进行优化。

  • 首先,在计算柱子面积的时候,我们可以将其放入循环体,减少一次循环。
  • 其次,注意到在经过最高点的时候,无论从左到右还是从右到左,剩余面积均为最高处的高度 * 数组剩下元素的个数

经过优化,豆子的面积 = 从左往右的局部最高面积 + 从右往左的局部最高面积 - 最高处柱子围成的长方形面积 - 柱子本身的面积

优化后的代码如下,使用循环进行分步累加

ans = 0
h1 = 0
h2 = 0
for i in range(len(height)):
    # 从左往右的局部最高点
    h1 = max(h1,height[i])
    # 从右往左的局部最高点
    h2 = max(h2,height[-i-1])
    # 从左往右 + 从右往左 - 柱子
    ans = ans + h1 + h2 -height[i]
计算出的面积 - 最高处柱子围成的面积即为所求
return  ans - len(height)*h1