container-with-most-water
文章目录
题目
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 and n is at least 2.
思路一
一开始我理解错了,以为是求相邻两条线段之间的面积最大,很快写了个方法提交上去,结果是Wrong Answer. 一看,原来是要任意两条线段之间的面积。
直观的想法,就是暴力,遍历所有的两两线段,计算出面积,取面积最大的,提交上去,结果是Time Exceed Limit. 下面是暴力的代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int max = 0;
for (int i = 0; i < height.size() - 1; ++i) {
for (int j = i + 1; j < height.size(); ++j) {
int min = this->min(height[i], height[j]);
// std::cout << "min:" << min << " " << min * (j - i) << " " << max << std::endl;
if (min * (j - i) > max) {
max = min * (j - i);
}
}
}
return max;
}
int min(int left, int right) {
return left < right ? left : right;
}
};
看来不能暴力,暴力的时间复杂度是O(n^2)。难道可以动态规划?但是想了一下,又没有重复子问题,看来这个方向不对。
思路二
看来这个问题,在暴力的方法中,肯定存在一些步骤是没有必要的,只能通过观察。我尝试画了一下,举了几个例子,发现:
- 每次取两条线段,面积的大小取决于最短的那一条,这叫做短板效应。
想起来查理芒格那句话: 反过来想,总是反过来想。所以初始的时候,取最左边和最右边的两条线段,是不是当前最大的面积呢?之前我思考的时候总是取最前面的几条。
最重要的发现是:
- 如果取两条线段,计算了面积之后,我们移动较长的那条线段,不管怎么移动,后面算出来的面积都不会比初始的面积大 因为面积的大小等于宽度乘以高度,一开始的宽度最大,由于面积的大小取决于最短的那一条, 所以后面取的线段,最短的高度一定是小于等于一开始的线段的高度, 可是宽度又变小了,那么面积肯定变小。
如果两条线段长度相等, 验证了一下,发现也符合这个规律。
那这样就好办了,每次移动线段时,找到最短的线段,往中间移动,因为没必要再移动最长的线段了,这样可以大大减少不必要的计算, 理论上O(n)的时间复杂度下就可以解决这个问题。
下面是这种思路的代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int max = 0;
int i = 0;
int j = height.size() - 1;
while (i < j) {
int min = this->min(height[i], height[j]);
// std::cout << "min:" << min << " " << min * (j - i) << " " << max << std::endl;
if (min * (j - i) > max) {
max = min * (j - i);
}
if (min == height[i]) {
++i;
} else if (min == height[j]) {
--j;
}
}
return max;
}
int min(int left, int right) {
return left < right ? left : right;
}
};
提交上去,居然打败了96%的人!
一大早起来刷道题,还是很有成就感的,不说废话了,收拾东西上班去。