当青训营遇上码上掘金,我选的是接青豆这道题目。
主题四
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
经典题:接雨水的变形,
几种方案都是参考lc题解,谢谢大佬的分享,这里记录下自己的学习思考。
按行累加
基本思想:对于每个行i,从找到第一个左侧墙壁开始计数height[j] >= i,遇到height[j] < i,计数++;遇到一个中间墙壁height[j] >= i,计数合入ans,计数清零。
for (int i = 1; i <= mx; ++i) {
bool isvaild = false;
int step = 0;
for (int j = 0; j < n; ++j) {
if (isvaild && height[j] < i) {
++step;
}
if (height[j] >= i) {
isvaild = true;
ans += step;
step = 0;
}
}
}
按照列累加
基本思想,找当前列i左侧的最大列leftmax,右侧最大列rightmax,当且仅当(i < min(rightmax, leftmax))时能接到豆子(即有凹槽),豆子数目为i - min(rightmax, leftmax)
int m = min(leftmax,rightmax);
if (height[i] < m) {
ans += (m - height[i]);
}
动态规划
按列累加优化,先遍历得到数组leftmax,rightmax;直接判断累加即可,这种方法lc样例执行速度最快。
vector<int> leftmax(n,0);
vector<int> rightmax(n,0);
for (int i = 1; i < n; ++i) {
leftmax[i] = max(leftmax[i-1],height[i-1]);
}
for (int i = n-2; i >= 0; --i) {
rightmax[i] = max(rightmax[i+1],height[i+1]);
}
单调栈(从大到小保存元素)
参考代码随想录
基本思想就是要找如下图所示的凹槽,遇到height[i] > height[st.top()]后需要继续取出下一位left,循环直到栈满足从大到小。
以下图为例说明
- 0,1,2位置正常进栈,满足从大到小
- height[3] > height[top()=2]
一次循环ans+=豆2,位置2退栈;位置3进栈 - height[4] > height[top()=3]
这个过程有两个循环:第一次位置3退栈;
第二次:
h = min(height[4],left=height[0])-height[1] = 2 - 1 = 1;
w = 4 - 0 - 1 = 3;
ans += 豆1,豆3,豆4;4进栈 - 结束 贴一个go的代码
func min (a int ,b int) int {
if a > b {
return b
}
return a
}
func solution(height []int) int{
ans := 0
st := make([]int,0)
for index, h := range height {
for len(st) > 0 && h > height[st[len(st)-1]] {
mid := st[len(st)-1]
st = st[:len(st)-1]
if len(st) > 0 {
left := st[len(st)-1]
h := min(height[left],h) - height[mid]
w := index - left - 1
ans += h * w
}
}
st = append(st,index)
}
return ans
}