11. 盛最多水的容器
题目描述
Given n
non-negative integers a1 , a2 , ..., an
, where each represents a point at coordinate(坐标) (i, ai )
. n
vertical(垂直的) lines are drawn such that the two endpoints of line i
is at (i, ai )
and (i, 0)
. Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant(倾斜) the container.
思路
这个问题和twoSum问题的思路类似,贪心算法(一般贪心算法的策略的正确性是用反证法进行验证的):从最大的宽度开始(计算得到这个宽度时的面积),计算下一阶段宽度(宽度减少了1)的面积时,我们贪心的去掉一个数(去掉当前范围两边的高度最小的那一个使得下一个宽度阶段下的面积尽可能最大.之所以从两边去掉数缩小宽度,而不从中间去掉一个数,是因为我们这样做可以使两个指针不跳过最优的点,即两个指针一定会经过最优的点)。
正确性证明:
假设(x,y)
为最优解,i
向右逐渐靠近x
,j
向左逐渐靠近y
,那么i
没有可能跳过x
,j
也没有可能跳过y
;
- 当
j==y
,i<x
时,那么此时必定有height[i]<height[j]
,如果不是这样,i
值就比x
值更优(如果height[i]>=height[j]
,则此时(i,j)
优于(x,y)
,宽度更宽,高度大于等于),与假设矛盾,即必定是i
向右移动直到i==x
; 当
i==x,
j>y`时,同理
另外,根据这个贪心操作过程,如果问题有多个解的话,我们的这个贪心进行过程不会错过任何一个解.
分析最优解的特点,从而得出两个指针如何移动才能靠近最优解,进而获得解题思路.
O
O
O O
O O
O O
O O
O O
x y
如图所示,假设(x,y)
为最优解,则y
右边的柱子的高度一定低于y
并且一定低于x
,x
左边的柱子
一定低于x
并且一定低于y
, 这是最优解两边柱子高度的分布规律,我们可以把握住这个规律,得出两个指针应该怎么移动.
代码实现
class Solution {
public:
int maxArea(vector<int> &height) {
if(height.size()==0 || height.size()==1)
return 0;
int maxArea = 0;
int l = 0;
int r = height.size()-1;
//和二分查找不一样,二分查找中的l,r为查找范围的边界,查找
//的候选值(一个值)是从l,r这个范围中取出来的,当然边界上的值也是
//需要我们验证的候选值,所以二分查找中需用<=。而这里的
//l,r是本身要找的两个不同的候选值下标,因为这两个值是不同的,
//所以需要用<,这里是从两边向中间扫。
while(l<r)
{
int cur = (r-l)*min(height[l],height[r]);
if(cur > maxArea)
maxArea = cur;
//如果两边一样高,则这两个相等的边
//都不可能是在以后的阶段最优解中的构成边(由于"最短原则",因为以后
//阶段产生的最优解的宽度
//一定小于当前两边相等时的宽度,如果这个最优解使用了这两个相等边中的
//某一个作为
//构成边,则面积的高度最多为x,所以"这个所谓的最优解"一定小于两边相等
//构成的面积)
//所以这两个相等的边中的任何一个都不可能构成以后阶段的最优解的构成元素,
//此时移动任何一个都没有问题,都不会"错过"
if(height[l] < height[r])
l++;
else if(height[l] == height[r])
{
l++;
r--;
}
else
r--;
}
return maxArea;
}
};