题意
- 就是在一个横坐标轴上,[0, n-1]每一个位置各立一个高为hi的柱子
- 让你,选择两个柱子,使得两个柱子和横坐标围成的容器可以存最多的水
思路
- 比较容易想到,两个柱子较低的那根决定了存水量
- 一种朴素的想法是,把每根柱子作为最短的那根,找到离它最远的那根比它高的柱子,得到一个存水量,遍历每一根就可以得到最大存水量
- 这样朴素来做,是O(n^2)的
- 基于这个思路,优化一下,可以想到,我们先把柱子从低到高排序,然后在遍历这个有序集合中的每个柱子时,我们维护两个变量l,r记录比该柱子高的最左和最右位置。
- 具体的方法,是用一个mark数组已经遍历过哪些位置的柱子,每遍历第i个柱子后,令mark[i]=1,然后让l和r分别向右、向左移动到第一个mark不是1的位置
- 这样移动总共只有n次吗,总的时间复杂度是排序的O(nlogn)
- 但是后来,发现自己二了。。其实有更简单的贪心方法。。
- 只要维护l,r两个变量分别记录左边和右边柱子位置,从两头向中间遍历即可。由于遍历时保证了两个柱子的距离是在减少的,并且较短的柱子决定了存水量,所以只要移动较短的柱子贪心即可
实现
class Solution {
public:
vector<pair<int, int> > vec;
int maxArea(vector<int>& height) {
for(int i = 0; i<height.size(); i++){
vec.push_back({height[i], i});
}
vector<int> mark(vec.size(), 0);
sort(vec.begin(), vec.end());
int l = 0, r = height.size() - 1;
int ans = 0;
for (int i=0;i<vec.size();i++){
ans = max(ans, vec[i].first * (r - vec[i].second));
ans = max(ans, vec[i].first * (vec[i].second - l));
mark[vec[i].second] = 1;
while (mark[l] == 1)
l++;
while (mark[r] == 1)
r--;
}
return ans;
}
};
class Solution {
public:
int maxArea(vector<int>& height) {
int ans = 0;
int l=0, r=height.size()-1;
vector<int>& a = height;
while (l < r){
if (a[l] <= a[r]){
ans = max(ans, a[l] * (r-l) );
l++;
}
else if (a[l] == a[r]){
ans = max(ans, a[l] * (r-l) );
l++;
r--;
}
else{
ans = max(ans, a[r] * (r-l) );
r--;
}
}
return ans;
}
};