攒青豆的实现

39 阅读2分钟

当青训营遇上码上掘金

攒青豆

题目

给定一个数组,表示一个条形图,计算按此排列的柱子能接多少青豆的问题 本题实例如下:

image.png

给定数组 {5, 0, 2, 1, 4, 0, 1, 0, 3},在这种情况下能接住17个单位的青豆。

函数签名如下:

func countBeans(vector []int) int {
 //function body
}

想法

一个显而易见的思路就是循环遍历该数组,计算每个单位能接住的青豆数量,计算i位置能接到的青豆数量。假设该位置左边最高的柱子长为lmax,右边最高的柱子长为rmax,并且都高于本位置柱子高度则该位置能接的青豆数量为height[i]=min( lmax, rmax)。对于边界条件处理,需要考虑如果本位置处于开头||结尾,则可以认为开头的左边柱子为‘0’,而结尾处右边的柱子为‘0’。

实现

对于这种简单算法编写代码如下:

func countBeans(vector []int) int {
   var count = 0
   for k, v := range vector {
      leftMost := findMost(vector[:k])
      rightMost := findMost(vector[k:])
      if v > leftMost || v > rightMost {
         continue
      }
      if leftMost > rightMost {
         count += rightMost - v
      }
      if rightMost > leftMost {
         count += leftMost - v
      }
   }
   return count
}

其中findMost函数实现传入一个切片,返回最大值。在寻找左边最高柱子时,传入该数组本位置之前的数组切片vector[:k],寻找最右边最高柱子时,传入该数组本位置之后的数组切片vector[k:]。如果本位置柱子比左边最高或者右边最高,则认为该区域无法接住青豆,跳出循环;如果本位置左边的最高柱子高于右边的最高柱子,则认为该处能接住rmax - height[i],反之,如果本位置右边的最高柱子高于左边的最高柱子,则认为该处能接住lmax - height[i]。使用count int 进行计数。

测试

编写合适的主函数进行测试:

func main() {
   vector := []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
   beans := countBeans(vector)
   fmt.Println(beans)
}

观察输出结果:

image.png

与计算结果一致。

更换初始数据为{0,1,0,2,1,0,1,3,2,1,2,1},测试结果为:

image.png

与预期结果一致

小结

本次「青训营 X 码上掘金」主题创作活动实现了攒青豆的一种简单思路,虽然实现了要求,但发现这种实现方法迭代到每个位置都重新计算一遍左右最高柱子,显然是不合理的,查阅资料发现可以通过“记事本”算法实现,直接把结果都提前计算出来,动态规划的方式减少计算次数,有效提升性能。