当青训营遇上码上掘金
攒青豆
题目
给定一个数组,表示一个条形图,计算按此排列的柱子能接多少青豆的问题 本题实例如下:
给定数组 {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)
}
观察输出结果:
与计算结果一致。
更换初始数据为{0,1,0,2,1,0,1,3,2,1,2,1},测试结果为:
与预期结果一致
小结
本次「青训营 X 码上掘金」主题创作活动实现了攒青豆的一种简单思路,虽然实现了要求,但发现这种实现方法迭代到每个位置都重新计算一遍左右最高柱子,显然是不合理的,查阅资料发现可以通过“记事本”算法实现,直接把结果都提前计算出来,动态规划的方式减少计算次数,有效提升性能。