码上掘金 | 青训营主题创作活动 —— 攒青豆主题

75 阅读3分钟

当青训营遇上码上掘金

主题 4 : 攒青豆

题目描述: 现有 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 个单位的青豆。

题解

攒青豆码上掘金代码地址

代码内容如下:

def save_green_beans(input_array):
  # 思想:寻找每一个点(横坐标x,纵坐标y)左右最近比该点更高的点。
  # 如果有,则计算:宽:左高点x1到右高点x2(x2-x1),高:左右高点中次高点与该点的差(min(y1,y2)-y)。以该点为最低点,可以填青豆的量为:等于(x2-x1)*(min(y1,y2)-y)
  # 所有可以填青豆的点求和,则为总可填青豆的和
  result = 0
  for loc, high in enumerate(input_array):
    left, left_high = find_higher_point(loc,0,input_array)
    
    if left == -1:
      continue
    
    right, right_high = find_higher_point(loc,1,input_array)
    
    if right == -1:
      continue

    result += (right-left-1) * (min(right_high, left_high)-high)
  
  return result


# loc 表示点的横坐标
# direction = 0 表示向左, direction = 1 表示向右
# input_array 表示输入数组
# 返回值为两个,第一个代表横坐标,第二个代表高度
def find_higher_point(loc, direction, input_array):
  # 如果在该点为边点,则认为没有比它更高的点
  if loc == 0 or loc == len(input_array)-1:
    return -1,-1

  # 向左寻找
  if direction == 0:
    for key in range(0,loc):
      if input_array[loc-key-1] >= input_array[loc]:
        return loc-key-1, input_array[loc-key-1]
    
    # 没有找到更大的
    return -1,-1
  
  # 向右寻找
  if direction == 1:
    for key in range(loc+1,len(input_array)):
      if input_array[key] >= input_array[loc]:
        return key, input_array[key]

    # 没有找到更大的
    return -1,-1

if __name__ == '__main__':
  input_array =  [5,0,2,1,4,0,1,0,3] 
  print("下面是示例:")
  print("输入数组为[5,0,2,1,4,0,1,0,3]")
  print("输出为:",save_green_beans(input_array))
   
#   print("下面可以自由输入数组进行测试。输入示例:如果您想输入[5,0,2],则依次输入5 0 2,数字间以空格隔开即可")
#   input_str = input("请输入:")
#   input_array = []
#   for item_str in input_str.split(" "):
#     input_array.append(int(item_str))
#   print("您输入的数组的结果为:",save_green_beans(input_array))

代码的输出为:

下面是示例:
输入数组为[5,0,2,1,4,0,1,0,3]
输出为: 17

代码的最后几行被注释掉了,码上掘金平台似乎没有提供命令行输入,在本地python环境可以解除注释,自定数组内容进行测试。

代码思路

首先观察图示,直观的印象和现实中凹容器盛东西很类似。

只要一个柱子左右都有比该柱子更高的柱子,该柱子与左右更高的柱子就可以形成凹容器。

该凹容器中可能包含更小的凹容器组成,且更小的凹容器可以盛的容积与大凹容器的计算面积相同。

所以可以尝试用递归的思想。

本文在发现递归的思想之后,又发现了凹容器容积的其他规律。即

寻找每一个柱子(横坐标x,纵坐标y)左右最近比该点更高的柱子。

如果有,则计算一个长方形容积:宽,左高柱子的横坐标x1到右高柱子的横坐标x2的举例,即x2-x1;高:左右两边的高柱子中较矮的柱子与该柱子的差值(min(y1,y2)-y)。

以该点为最低点,可以填青豆的量为:等于(x2-x1)*(min(y1,y2)-y)

所有可以填青豆的点求和,则为总可填青豆的和

则容积可以解构为如下图所示

image.png

本文的代码中,在以其中某一柱子为中心,向左右分别检查最邻近的更高的柱子时,使用直接依次遍历的方式。如果考虑减少运行时间,则可以考虑使用数据结构记录遍历过的结果,帮助搜索邻近的更高的柱子。