当青训营遇上码上掘金
青训营主题4
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
解决代码示例code.juejin.cn/api/raw/718…
这个问题,如果我们想象成青豆向上往下坠落,而多出来的豌豆会滑出这个空间,从而计算体积的话,这个动态过程是很难模拟的...我通过观察发现,如果我们从左边和右边去观察,你会发现你是看不见青豆的,这让我萌发出来一个想法,若青豆是填满了柱子所在的空间,然后通过消除法不断的消除青豆,最后剩下来的就是能够接住的青豆,这样的思考方式是静态的,而不是动态的...
接下来 我用我画的一个示例图来展示我的想法
青豆只会从左边和右边滑落
若是右边的豆子的滑落,则从右向左数第一个最高柱子是从右滑落的左边界
并且,当我们从右向左检索看见最高柱子时,这个柱子后面的青豆绝不可能从右滑落了
按照这个逻辑,我们可以最开始就把这个空间想象成柱子数为长,柱子最高长度为高的长方体,填满了青豆,这个时候,我们轻而易举可以算出豌豆数量count(长*宽-柱子高度和)
想象左边和右边摆放着一台水平激光照射装置,凡是照到了的豌豆全部消除(凡是消除,就让总的count数-此部分数量)
接下来 我使用go语言来实现此功能
var n int
fmt.Scanf("%d\n", &n)
var height []int
var k int
max := 0
sum := 0
首先定义初始的值
n为柱子数量(包括高度为0的柱子数)
height是柱子高度统计切片
k是中间量
max是最高柱子高度
sum是柱子高度合计
for i := n; i > 0; i-- {
fmt.Scanf("%d", &k)
if k > max {
max = k
}
sum += k
height = append(height, k)
}
通过此循环来输入柱子高度值,并统计出max的值
count := n*max - sum
count值是目前所有的青豆数
先让右边的激光向左照射,注意,为什么是count-=(...)*(n-1-l)
因为l与r的作用是通过此下标查到其柱子高度,算出清除高度差作为长方形消除的高,而长永远是左边界到最右边(下标为n-1)的长度,以右边消除是(n-1-l)
l := n - 1
r := n - 1
for {
l--
if l < 0 {
break
}
if height[l] > height[r] {
count -= (height[l] - height[r]) * (n - 1 - l) //总数中减去那块长方形青豆
r = l //让l成为起点
}
if height[r] == max { //若初始边界已经是最大了,则这个柱子已经抵挡所有激光,后面都会被保护
break
}
}
l和r都是下标(l<r),l一直往左边走,r不动,只要出现了height[l]>height[r]的情况,就开始清除,并让r=l,继续清除,只要l<0或者r=max的时候,就没有继续往下走的必要了(因为豆子不可能再往这个方向落了)
模拟一下上面代码的清除过程
左边也是一样,注意,为什么count-=(...)*r
下标0与r之间一定是清除长方体的长,高度是height[r]-height[l]
l = 0
r = 0
for {
r++
if r > n-1 {
break
}
if height[r] > height[l] {
count -= (height[r] - height[l]) * r
l = r
}
if height[l] == max {
break
}
}
模拟一下清除的过程
此图n=12 {3,1,0,5,0,2,5,0,4,0,0,2} 青豆应剩21
函数输出为21
证实是没有问题的