当青训营遇上码上掘金
好了参加了冬令营,赶紧参加点活动保命不摆烂好吧,感觉题四简单一点就先写了,这个还是比较经典的动态规划,其实就是leetcode经典的接雨水的动态规划题,感觉dp就是往两个大方向走一个是维度广,出点什么二维三维的还有一个就是这种加点条件往深了走,虽然很菜啊,但是比较喜欢dp所以先写个dp的思路
动态规划
这个方法使用了前缀最大值+后缀最大值 就是取每一个柱子理想的前后攒豆数再减去柱子高度,就是实际上可以承载的豆子数
所以我们假设下标 i 处能攒的豆子等于下标 i 处的豆子能到达的最大高度减去 heigh[i]。所以,就可以直接遍历,分别从左和从右扫描并分别记录左边和右边的最大高度,然后计算每个下标位置能攒的豆子的数量,时间复杂度为O(n^2)。
但是如果可以知道每个位置两边的最大高度,则可以用遍历一遍的时间得到能接的豆子量,所以自然而然的可以想到用dp来记录。前缀和可以参考LeetCode买卖股票的那题,后缀参考最大元素替换。
可以先记录下标 i 的前缀最值和后缀最值,让再求其中的小者减去柱子高度就是当前柱子能攒的豆子数。
我们来看看代码怎么写,设 n 为数组的长度,start为前缀最大值,after为后缀最大值
go语言的代码 攒青豆 - 码上掘金 (juejin.cn)
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
}
func PreMax(height,start []int, n int ){
for i := 0; i < n; i++{
if i == 0{
start[i] = height[i]
}else {
// 当不是第一位时如果之前的最大高度比当前高则保留,反之替换
start[i] = max(start[i - 1],height[i])
}
}
}
func BehMax(height,after []int, n int ){
for i := n - 1; i >= 0; i--{
if i == n - 1{
after[i] = height[i]
}else {
// 当不是第一位时如果之前的最大高度比当前高则保留,反之替换
after[i] = max(after[i + 1], height[i])
}
}
}
func trap(height []int) int {
n := len(height)
start := make([]int,n)
after := make([]int,n)
var res int
PreMax(height,start,n)
BehMax(height,after,n)
for i , _ := range height{
res += min(start[i],after[i]) - height[i]
}
return res
}
时间复杂度O(n) 空间复杂度O(n)
双指针
当然不用dp还可以用双指针嘛方法太多辣就写这两个好了。那么用同向快慢指针也可以就是遇到高的把之前的算一遍,但是还是会有点小问题,所以我们这里可以考虑面对面的两个指针,左指针在最左端,右指针在最右端,那么一开始假设中间没有柱子,就可以接(r - l - 1) * min(l,r) 的豆子记为n,然后我们让矮的一侧指针移动,中间分为两种情况
之间没有比l与r更高的柱子
那么能接的青豆数就是 n 减去之间柱子的高度
之间有比l与r更高的柱子
那么移动之前的豆子数存起来将新的柱子为开始重复之前的步骤
时间复杂度O(n) 空间复杂度O(1)