原题
接雨水 Problem Description
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
Sample1
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。Sample2
输入:height = [4,2,0,3,2,5]
输出:9Tip
n == height.length
0 <= n <= 3 * 104
0 <= height[i] <= 105
思路分析:
①确定状态:设dp[i]表示height数组下标0~i的元素最多接多少雨水。
②边界条件:由于至少要3个柱子才能接到雨水,因此dp[0] = 0,dp[1] = 0。
③状态转移:这一步是最关键的。当前下标为i,要从i开始往左找,找到一个能装到最多水的柱子j。
有两种情况:
第一是左边的柱子全部低于当前柱子,此时要找左边柱子中最高的柱子;
第二是左边有高于当前柱子的柱子,则要从当前柱子向左找到第一根高于当前柱子的柱子。
情况1
情况2
可以写一个函数寻找柱子j。
int find(vector<int>& height, int index){
//从右往左找到第一个大于该元素的
int i, max = -1, maxIndex = -1;
for (i = index-1; i >= 0; i--){
if (height[i] > height[index]){
return i;
}
if (height[i] > max){
max = height[i];
maxIndex = i;
}
}
//如果没有大于该元素的,则找剩下的元素中最大的
return maxIndex;
}
已知i与j,很容易算出i和j柱子围城的区域能装的雨水量,
令V1为当i柱子与j柱子间没有柱子时,两柱子所围成的区域能装的雨水量,则
V1 = min(height[i], height[j])*(i-j-1)
令V2 = sum(height[k]), j < k < i
则i柱子与j柱子间一共能装V = V1-V2
求出了i柱子与j柱子间能装的雨水量,再加上0~j号柱子能装的雨水量,就是0~j号柱子间能装的雨水量,即dp[i] = dp[j] + V
;
AC代码
class Solution {
public:
int find(vector<int>& height, int index){
//从右往左找到第一个大于该元素的
int i, max = -1, maxIndex = -1;
for (i = index-1; i >= 0; i--){
if (height[i] > height[index]){
return i;
}
if (height[i] > max){
max = height[i];
maxIndex = i;
}
}
//如果没有大于该元素的,则找剩下的元素中最大的
return maxIndex;
}
int trap(vector<int>& height) {
//dp[i]表示下标0~i的元素最多接多少雨水
int *dp = new int[height.size()+5];
dp[0] = 0;
dp[1] = 0;
for (int i = 2; i < height.size(); i++){
int left = find(height, i);
int h = (height[left]<height[i]?height[left]:height[i]);
int v = (i-left-1)*h;
for (int j = left+1; j < i; j++){
v -= height[j];
}
dp[i] = v + dp[left];
}
return (height.size()>0?dp[height.size()-1]:0);
}
};