当青训营遇上码上掘金,使用官方推广码上掘金平台,将代码上传到浏览器上并运行。
题目描述
-
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路
经常刷题的童鞋应该已经看出来了,其实就是翻版的42. 接雨水 - 力扣(Leetcode),个人觉得用单调栈来解决比较简单。
维护一个单调栈,单调栈存储的是下标,要求下标对应元素在栈中是单调递减的。
从左到右遍历数组,若栈空则下标值入栈,若当前元素小于栈顶元素则下标值入栈;
若当前元素大于栈顶元素,还需要比较栈顶的第二个元素。若第二个元素小于当前元素,则可以计算该区域内可堆积的青豆,然后弹出栈顶元素,继续之前的步骤。直到第二个元素大于当前元素,或栈中已经没有元素了,则计算青豆,并将当前元素入栈。
其实上面那段思路很多地方都有不同的描述,但是一般这样的描述都比较抽象我自己也不太愿意看,需要自己慢慢理顺了。这里用一张图标出根据单调栈计算面积的次序,可以更好的理解代码。
代码
collectGreenPeas - 码上掘金 (juejin.cn)
这里不知道怎么在码上掘金平台控制输入。这里严格按照输入为height = [5,0,2,1,4,0,1,0,3]
处理,此外由于以前算法题ACM模式都是用c++处理,这里尝试用golang处理,使用bufio包,不知道还有没有更好的写法。
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func peas(height []int) (ans int) {
stack := []int{}
for index, h := range height {
for len(stack) > 0 && h > height[stack[len(stack)-1]] {
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if len(stack) == 0 {
// 剩余栈空, 则无法装青豆
break
}
// 计算一次不高于当前桶最左端可装的青豆
left := stack[len(stack)-1]
curWidth := index - left - 1
curHeight := min(height[left], h) - height[top]
ans += curWidth * curHeight
}
// 若栈空 或 当前元素小于栈顶元素,则将当前元素的index入栈
stack = append(stack, index)
}
return
}
func main() {
input := bufio.NewReader(os.Stdin)
line, err := input.ReadString('\n')
if err != nil {
log.Fatal(err)
}
fmt.Println(len(line))
// windows需要去除字符串尾部的\r\n换行符
line = strings.Trim(line, "\r\n")
// 去除方括号
line = string([]byte(line)[10 : len(line)-1])
// 分割字符串
strDatas := strings.Split(line, ",")
// 转换为数组
datas := make([]int, len(strDatas))
for i := 0; i < len(strDatas); i++ {
datas[i], err = strconv.Atoi(strDatas[i])
if err != nil {
log.Fatal(err)
}
}
fmt.Println(peas(datas))
}