题面:
思路:
能接雨水的点,必然是比两边都低(小)的点。有两种思路,一种是直接计算每个点的最大贡献(也就是每个点在纵向上最多能接多少水),另一种就是计算每个点在横向上有没有贡献。
第一种思路,就需要我们能拿到每个点左右两边最高的高度,这样就能计算每个点在纵向上能接多少水,相当于木桶效应。
第二种思路,对于每个点,则需要判断它左右两边是不是都有比它高的点,每次计算横向上局部能接到水的区域。
官方题解挺好的,具体不再赘述
1. 单调栈
就是第二种思路,每次更新横向上能接到的水。
int trap(vector<int>& height) {
int ans = 0, n = (int)height.size();
stack<int> stk;
for(int i = 0; i < n; ++ i) {
while(!stk.empty() && height[i] > height[stk.top()]) {
// 得到当前低点
int buttom = stk.top(); stk.pop();
if(!stk.empty()) {
int j = stk.top();
// 如果 height[j] == height[bottom]
// 就说明 bottom 的左边还没有出现凸出来让bottom能接到水的边边
ans += (i - j - 1) * (min(height[i], height[j]) - height[buttom]);
}
}
stk.push(i);
}
return ans;
}
2. 动态规划
第一种思路,要拿到每个点左右两边的最大高度,就可以考虑线性DP的思想去记录当前点左右两边的最大高度。
l
e
f
t
M
a
x
[
i
]
=
m
a
x
(
l
e
f
t
M
a
x
[
i
−
1
]
,
h
e
i
g
h
t
[
i
]
)
r
i
g
h
t
M
a
x
[
i
]
=
m
a
x
(
r
i
g
h
t
M
a
x
[
i
+
1
]
,
h
e
i
g
h
t
[
i
]
)
g
o
t
W
a
t
e
r
[
i
]
=
m
i
n
(
l
e
f
t
M
a
x
[
i
]
,
r
i
g
h
t
M
a
x
[
i
]
)
−
h
e
i
g
h
t
[
i
]
leftMax[i]=max(leftMax[i-1],height[i])\\ rightMax[i]=max(rightMax[i+1],height[i])\\ gotWater[i] = min(leftMax[i],\ rightMax[i])-height[i]
leftMax[i]=max(leftMax[i−1],height[i])rightMax[i]=max(rightMax[i+1],height[i])gotWater[i]=min(leftMax[i], rightMax[i])−height[i]
int trap(vector<int>& height) {
int ans = 0, n = (int)height.size();
vector<int> leftMax(n), rightMax(n);
leftMax[0] = height[0]; rightMax[n - 1] = height[n - 1];
for(int i = 1; i < n; ++ i) leftMax[i] = max(leftMax[i - 1], height[i]);
for(int i = n - 2; i >= 0; -- i) rightMax[i] = max(rightMax[i + 1], height[i]);
for(int i = 1; i < n - 1; ++ i)
ans += min(leftMax[i], rightMax[i]) - height[i];
return ans;
}
双指针
使用双指针和临时变量优化掉
l
e
f
t
M
a
x
leftMax
leftMax 和
r
i
g
h
t
M
a
x
rightMax
rightMax 两个数组。
官方题解说:如果
h
e
i
g
h
t
[
l
e
f
t
]
<
h
e
i
g
h
t
[
r
i
g
h
t
]
height[left]<height[right]
height[left]<height[right],则必有
l
e
f
t
M
a
x
<
r
i
g
h
t
M
a
x
leftMax<rightMax
leftMax<rightMax。
这主要是因为,我们每次移动的都是
h
e
i
g
h
t
height
height 较小的指针,因此如果
l
e
f
t
M
a
x
leftMax
leftMax 或
r
i
g
h
t
M
a
x
rightMax
rightMax 有更新,则更新了的点
l
e
f
t
left
left 或
r
i
g
h
t
right
right 在
l
e
f
t
M
a
x
leftMax
leftMax 或
r
i
g
h
t
M
a
x
rightMax
rightMax 得到新的更新之前会停留一阵子。因此如果
h
e
i
g
h
t
[
l
e
f
t
]
<
h
e
i
g
h
t
[
r
i
g
h
t
]
height[left]<height[right]
height[left]<height[right],则必有
l
e
f
t
M
a
x
<
r
i
g
h
t
M
a
x
leftMax<rightMax
leftMax<rightMax。
int trap(vector<int>& height) {
int ans = 0, n = (int)height.size();
int left = 0, right = n - 1;
int leftMax = height[0], rightMax = height[n - 1];
while(left < right) {
leftMax = max(leftMax, height[left]);
rightMax = max(rightMax, height[right]);
if(height[left] < height[right])
ans += min(leftMax, rightMax) - height[left ++];
else
ans += min(leftMax, rightMax) - height[right --];
}
return ans;
}