给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
先从简单的问题 入手,假设柱子是:
[1, 0, 2]
我们很容易想到
- 选取一根柱子
l
,使得右边边柱子l+1
低于l
- 从
l+2
开始选取第一根大于等于l
高度的柱子r
- 计算中间的水量,加入答案
- 如果找不到柱子高于
l
,那么选取l+1
作为l
,重新开始
对于“左边小于右边”这种情况,我们很容易填满
可是如果出现这种情况:
我们只能填满蓝色区域,红色区域被忽略了
那么我们先填满蓝色区域,再从右到左模拟一次就好了
基本思路:
- 从左到右枚举左端点,找第一个大于等于左端点的右端点,计算水量,左端点移到右端点,重复
- 填满1中计算过的地方,填充到上限: l, r 两个端点的最小值
- 从右到左枚举右端点,找第一个大于等于右端点的左端点,计算水量,右端点移到左端点,重复
代码
class Solution {
public:
int trap(vector<int>& height)
{
if(height.size()<3) return 0;
int l=0, r=0, ans=0;
// 从左到右
while(l+2<height.size())
{
// 找合法左端点
while(l+2<height.size() && height[l+1]>=height[l]) l++;
r=l+2;
// 找第一个大于等于左端点的右端点
while(r<height.size() && height[r]<height[l]) r++;
if(r==height.size()){l++; continue;}
// 计算,填坑
for(int i=l+1; i<=r-1; i++)
{
ans += min(height[l], height[r])-height[i];
height[i] = min(height[l], height[r]);
}
l=r;
}
// 从右到左
r = height.size()-1;
while(r-2>=0)
{
while(r-2>=0 && height[r-1]>=height[r]) r--;
l=r-2;
while(l>=0 && height[l]<height[r]) l--;
if(l==-1){r--; continue;}
for(int i=l+1; i<=r-1; i++)
ans += min(height[l], height[r])-height[i];
r=l;
}
return ans;
}
};