当青训营遇上码上掘金
青训营结营证书和礼品都需要青豆来兑换,攒青豆对于广大参加青训营的友友们来说都是一件重要的事情!主题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 个单位的青豆。
不能说完全一样,只能说和接雨水差不多
解法
双指针
按列来计算的话感觉理解容易一些,,宽度肯定是1,把每一列的青豆高度求出来就可以了。
每一列青豆的高度取决于该列左面最高柱子和右面最高柱子中小的那个,比如假设高度2的柱子为第三列,求第三列的青豆高度过程如下:
首先求列3左面最高的柱子,是列1,高度为5;
然后是列3右面最高的柱子,是列5,高度为4;
列3自己柱子高度是2,那么列3青豆的高度就是 min(列1,列5)-列3=2,那么列3的青豆就为 高 x 宽 = 2 。
同样的方法,全遍历一遍就可以了,然后相加,并且第一个柱子和最后一个柱子没有,不要算。
func doublePointer(columns []int) int {
n := len(columns)
left, right := 0, n-1
leftMax, rightMax := 0, 0
res := 0
for left < right {
if columns[left] < columns[right] {
if columns[left] >= leftMax {
leftMax = columns[left]
} else {
res += leftMax - columns[left]
}
left++
} else {
if columns[right] >= rightMax{
rightMax = columns[right]
} else {
res += rightMax - columns[right]
}
right--
}
}
return res
}
动态规划
双指针解法是记录左面柱子的最高高度和右边柱子的最高高度,就能计算所在位置的豆,但是双指针会把每个柱子的两边都遍历一遍,用动态规划就可以避免重复计算。
把每一个柱子位置上的最高高度位置记录在数组上。当前位置,左面的最高高度是前一个位置的左面最高高度和本高度的最大值,右面同理。
从左向右遍历:leftMax[i] = max(height[i], leftMax[i - 1])
从右向左遍历:rightMax[i] = max(height[i], rightMax[i + 1])
func trap(height []int) (ans int) {
n := len(height)
if n == 0 {
return
}
leftMax := make([]int, n)
leftMax[0] = height[0]
for i := 1; i < n; i++ {
leftMax[i] = max(leftMax[i-1], height[i])
}
rightMax := make([]int, n)
rightMax[n-1] = height[n-1]
for i := n - 2; i >= 0; i-- {
rightMax[i] = max(rightMax[i+1], height[i])
}
for i, h := range height {
ans += min(leftMax[i], rightMax[i]) - h
}
return
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
结语
以上内容就是「青训营 X 码上掘金」主题创作活动入营版的主题4的题目和解决办法了,总体来说还是第二种动态规划的方式性能更好,但是第二种是建立在第一种双指针之上的,双指针也更好理解一些。
希望参加青训营的友友们都可以攒到自己希望的青豆数目!