当青训营遇上码上掘金
先贴上完整代码
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func MaxGreenBean(height []int) int {
n := len(height)
if n == 0 {
return 0
}
res,l := 0,0
for i := 1; i < n; i++ {
if height[i] >= height[l] {
l = i
continue
}
k := i + 1
for k < n && height[k] <= height[k-1] {
k++
}
mid := k - 1
for k < n-1 && height[k] <= height[k+1] {
k++
}
if k == n {
break
}
r := k
h := min(height[r], height[l])
j := mid
for j > l && height[j] < height[r] {
res += h - height[j]
height[j] = h
j--
}
j = mid + 1
for j < n && h > height[j] && height[j] < height[r] {
res += h - height[j]
height[j] = h
j++
}
if height[r] > height[l] {
l = r
}
i = k
}
return res
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func main() {
array := make([]int, 0)
fmt.Println("输入柱子的高度序列,以逗号或空格隔开,按回车计算")
reader := bufio.NewReader(os.Stdin)
height, _, _ := reader.ReadLine()
for _, v := range height {
if v != ' ' && v != ',' {
num, _ := strconv.Atoi(string(v))
array = append(array, num)
}
}
fmt.Println("能接的豆子数量是", MaxGreenBean(array))
}
思路讲解
1.对于一段高低不同的柱子高度序列,先找到每个能接豆子的坑里面最低谷的那个柱子的下标mid,然后沿着mid的左右两边分别延申找到左右的最高峰,实际处理时,每一次计算以mid为中心,以[l, r]为一段,根据高度差计算每一列能装多少豆子,然后将柱子高度差所构成的坑也填平掉。这样,当下一次继续以l为左边峰的时, 这里都会是平地,就不会重复计算需要填充的豆子了
2.这道题也可以使用滑动窗口的思想来解决
第一步:从左到右开始遍历区间,找到大于或者等于右边的区域,然后计算可以接到水的体积。如果找不到就直接调出循环,因为在第二步时,它一定可以遍历计算到。
第二部:从右到左开始遍历区间,找到大于右边高度的区域,然后计算面积,如果找不到,就直接返回值,因为对于此时,第一次遍历一定可以计算到这个区域。
3.单调栈,维护一个非严格的递减单调递栈,从前往后遍历每个数,若当前的数小于等于栈顶元素那么直接入栈,若大于栈顶元素,那么代表必然会有积水,因为栈顶元素下面还有比它大的元素,那么此时能够存储的豆子数目为找到的左右两柱子最短的那一根乘上宽度