当青训营遇上码上掘金
攒青豆
原题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
分析
以下为上图例子的解析: 输入:height = [5,0,2,1,4,0,1,0,3] 输出:17 解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
以上图为例,我们可以一根一根柱子来分析能装下的青豆。
第一根柱子高度为5,它的左边最高的为0,右边最高的为4,看得出来,左右都没有比它高,故这个位置并不能装下任何青豆。
第二根柱子的高度为0,它的左边最高的为5,右边最高的为4,由图可知,它能装下4 - 0 = 4个单位青豆。
第三根柱子的高度为2,它的左边最高的为5,右边最高的为4,由图可知,它能装下4 - 2 = 4个单位青豆。
第五根柱子的高度为4,它的左边最高的为5,右边最高的为3,由图可知,4 > 3,它能装下0个单位青豆。
...
以此类推,我们可以发现,每个位置能装下的青豆取决于三个因素:左边最高的柱子长度,右边最高的柱子长度和当前位置柱子的长度。i 位置可装下的青豆数目为
min(maxLeft[i], maxRight[i]) - height[i]
且当当前位置的高度大于min(maxLeft[i], maxRight[i])时,min(maxLeft[i], maxRight[i]) - height[i]会得到一个负数,我们取0。
最后相加每个位置计算出来的值即可。
我们要遍历整个height数组,故时间复杂度为O(n), 需要存储每个位置的左最高,右最高,故空间复杂度为O(n)。
具体实现
package main
import (
"fmt"
)
func main() {
const LEN = 9 // 数组长度
height := [LEN]int{5, 0, 2, 1, 4, 0, 1, 0, 3} // 高度数组
var maxLeft, maxRight [LEN]int // 每个位置上左边最高的高度和右边最高的高度
for i := 1; i < LEN; i++ {
maxLeft[i] = Max(height[i - 1], maxLeft[i - 1])
maxRight[LEN - i - 1] = Max(height[LEN - i], maxRight[LEN - i])
}
ans := 0
for i:= 0; i < LEN; i++ {
tmp := Min(maxLeft[i], maxRight[i]) - height[i]
if tmp > 0 {// 如果青豆数大于0, 表示符合条件
ans += tmp
}
}
fmt.Println(ans)
}
// 返回两个数之间小的数
func Max(x, y int) int {
if x < y {
return y
}
return x
}
// 返回两个数之间大的数
func Min(x, y int) int {
if x > y {
return y
}
return x
}