当青训营遇上码上掘金
代码块中包含大量注释,也是文字!
主题 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 个单位的青豆。
作者:青训营官方账号
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
输入格式:
5 0 2 1 4 0 1 0 3
0 1 0 2 1 0 1 3 2 1 2 1
4 2 0 3 2 5
2 0 2
输出格式:
17
6
9
2
类似木桶理论即短板效应,次大值制约了最大值的发挥。
令n=height.length, 初始时begin=0, end=n-1。
找出区间[begin, end]之内的最大值max1及其索引max1i,次大值max2及其所有索引max2is中的最小值nb、最大值ne。然后根据max1i、nb、ne这3个索引将区间[begin, end]分割为[begin, p1](递归), [p1, p2](可计算青豆数量), [p2, end](递归),分而治之。
-
max1 = max2max1i在max2is中,即nb<=max1i<=ne. 最大值在区间[nb, ne]内,则[nb, ne]可计算青豆数量,区间分割为[begin, nb], [nb, ne], [ne, end]。 -
max1 > max2Ⅰ.
max1i < nb最大值在区间[nb, ne]外,则[max1i, ne]可计算青豆数量,区间分割为[begin, max1i], [max1i, ne], [ne, end]。Ⅱ.
max1i > ne最大值在区间[nb, ne]外,则[nb, max1i]可计算青豆数量,区间分割为[begin, nb], [nb, max1i], [max1i, end]。Ⅲ.
nb < max1i < ne最大值在区间[nb, ne]内,则[nb, ne]可计算青豆数量,区间分割为[begin, nb], [nb, ne], [ne, end]。
package main
// import "fmt"
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
// height := []int{5,0,2,1,4,0,1,0,3}
// ret := dc(height, 0, len(height)-1)
// fmt.Println(ret, height)
// 从标准输入读取 height 数组
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
in := strings.Split(scanner.Text(), " ")
height := []int{}
for _, item := range in {
num, _ := strconv.Atoi(item)
height = append(height, num)
}
ret := dc(height, 0, len(height)-1)
// fmt.Println(ret, height)
fmt.Println(ret)
}
}
// divide and conquer,分治。
// [begin, end]内能攒的青豆
func dc(height []int, begin int, end int) int {
if begin >= end {
return 0
}
// 最大值、次大值
max1, max2 := height[begin], -1
max1i := begin
for i := begin + 1; i <= end; i++ {
if max1 <= height[i] {
max2 = max1
max1, max1i = height[i], i
} else if max2 < height[i] {
max2 = height[i]
}
}
// 次大值的所有索引
max2is := []int{}
for i := begin; i <= end; i++ {
if height[i] == max2 {
max2is = append(max2is, i)
}
}
ret := 0
nb, ne := max2is[0], max2is[len(max2is)-1]
// p1, p2是2个分割点,从max1i、nb、ne中选出
// 分割成[begin, p1] [p1, p2] [p2, end]三段
var p1, p2 int
// max1 >= max2
// max1在[nb, ne]里面
if max1 == max2 {
p1, p2 = nb, ne
// max1 > max2
// max1在[nb, ne]外面
} else if max1i < nb {
p1, p2 = max1i, ne
// max1在[nb, ne]外面
} else if ne < max1i {
p1, p2 = nb, max1i
// max1在[nb, ne]里面
} else {
p1, p2 = nb, ne
}
ret += dc(height, begin, p1)
ret += comp(height, p1, p2, max2)
ret += dc(height, p2, end)
// fmt.Println(begin, end, max1, max1i, max2, max2is, ret)
return ret
}
// 计算区间内能接到的青豆
func comp(height []int, begin int, end int, target int) int {
ret := 0
for i := begin; i <= end; i++ {
if height[i] < target {
ret += target - height[i]
}
}
return ret
}