题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积
刷过《剑指offer》就知道,就是接雨水问题,难道套了个马甲我就不认识你了么?这道题基本上是初见杀--没遇到之前只有暴力解法的思路(直接TLE= =),因此令人印象深刻。
方法一: 暴力
计算每一根柱子可以存青豆量,然后所有的加起来就是最终的存的青豆量。其中,每根柱子存的青豆量等于该柱子左右两侧最大高度中的较小者减去当前柱子的高度。
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
vector<int> height;
int main() {
// 输入处理
cin >> n;
for (int i = 0; i < n; i ++) {
int x;
scanf("%d", &x);
height.push_back(x);
}
int ans = 0;
// 遍历每个柱子
for (int i = 1; i < n - 1; i++) {
int leftMax = 0, rightMax = 0;
// 计算当前柱子左侧的柱子中的最大高度
for (int j = 0; j <= i; j++) {
leftMax = Math.max(leftMax, height[j]);
}
// 计算当前柱子右侧的柱子中的最大高度
for (int j = i; j < n; j++) {
rightMax = Math.max(rightMax, height[j]);
}
// 存青豆量 = 当前柱子左右两边最大高度的较小者 - 当前柱子的高度
res += Math.min(leftMax, rightMax) - height[i];
}
cout << ans;
return 0;
复杂度分析
- 时间复杂度: , 两重循环遍历。
- 空间复杂度:
方法二: 双指针
对数组中的所有元素进行一次预处理:
- 先从右往左遍历,找到每一根柱子右侧最高的柱子;
- 再从左往右遍历,找到每一根柱子左侧最高的柱子。
因此,对于每一根柱子,能接住青豆的量,就是左右两侧最高柱子的最小值与当前柱子的高度的差值,最后,将所有的柱子能接住的青豆量相加即可。
可以维护两个指针 和 ,以及两个变量 和 , 使用和 的值更新 和 的值,这样就可以省去dp要维护的数组。
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
vector<int> height;
int main() {
// 输入处理
cin >> n;
for (int i = 0; i < n; i ++) {
int x;
scanf("%d", &x);
height.push_back(x);
}
//动态规划 + 双指针
int ans = 0;
int left = 0;
int right = height.size() - 1;
int leftMax = 0, rightMax = 0;
while (left < right)
leftMax = max(leftMax, height[left]);
rightMax = max(rightMax, height[right]);
if(height[left] < height[right]){
ans += leftMax - height[left];
left++;
}
else {
ans += rightMax - height[right];
right--;
}
}
cout << ans;
return 0;
}
复杂度
- 时间复杂度: ,n为数组height的长度。只需要遍历数组height一次
- 空间复杂度: ,n为数组height的长度。