一开始写的算法,只是穷举法,结果导致在最后一个大的input case总司TLE,后来通过剪枝,勉强28ms通过。如下是我的代码:
int maxArea(vector<int>& height) {
int i, j, maxArea = 0, tmp, len = (int)height.size(), loopMax = 0, maxStart = 0, maxMin = 0;;
for (i=0; i<len-1; i++) {
if (height.at(i) <= maxStart) { // <span style="font-family: Arial, Helvetica, sans-serif;">maxStart当前最大数值时,起始线的长度</span>
continue;
}
loopMax = 0;
for (j=len-1; j>=i+maxArea/height.at(i)+1; j--) { // 为什么用递减?是为了能够利用j和i之间距离越来越小这一特点,便于剪枝
if (height.at(j) <= loopMax || height.at(j) <= maxMin) {
continue;
} else {
loopMax = height.at(j);
}
tmp = min(height.at(i), height.at(j)) * (j-i) ;
if (tmp > maxArea) {
maxArea = tmp;
maxStart = height.at(i);
maxMin = min(height.at(i), height.at(j));
}
}
}
return maxArea;
}
后来看了一下网上的代码,才发现这道题目的解法其实是贪心。贪心方法的特点是解法容易,复杂度一般是O(n^2)乃至O(n),但是证明较难,这道题目,我在Google上几乎没找到特别严谨并且易于理解的证明。不过其大致思想可以解释如下:
1. 最宽的容器(通过第一根和最后一根线构造)是一个非常好的候选者,因为它的宽最大。它的高度为第一根线和第二根线中较短的那根线的长度。
2. 所有其他的容器都比(1)中最宽的容器窄,因此为了能够多装水,构造容器的两根线中最短的那根线应该尽可能长。
3. 二根线中,较短的那根线不符合(2),应该直接被移除。
注意上述,论述中的(3)并不严谨,在这里只是方便大家理解和记忆。贪心的代码如下:
int maxArea(vector<int>& height) {
int maxArea = 0, i = 0, j = (int)height.size()-1;
while (i < j) {
maxArea = max((height.at(i), height.at(j)) * (j-i), maxArea);
if (height.at(i) < height.at(j)) {
i++;
} else {
j++;
}
}
return maxArea;
}
思路:
由于ai和aj (i<j) 组成的container的面积:S(i,j) = min(ai, aj) * (j-i)
所以对于任何S(i'>=i, j'<=j) >= S(i,j),由于j'-i' <= j-i,必然要有min(ai',aj')>=min(ai,aj)才行。同样可以采用头尾双指针向中间移动:
当a(left) < a(right)时,对任何j<right来说
(1) min(a(left),aj) <= a(left) = min(a(left), a(right))
(2) j-left < right-left
所以S(left, right) > S(left, j<right)。排除了所有以left为左边界的组合,因此需要右移left。同理,当a(left) > a(right)时,需要左移right。而当a(left) = a(right)时,需要同时移动left和right。
思路整理:
left = 0, right = n-1
(1) a[left] < a[right], left++
(2) a[left] > a[right], right--
(3) a[left] = a[right], left++, right--
终止条件:left>-right
class Solution {
public:
int maxArea(vector<int> &height) {
int mxArea = 0;
int left = 0, right = height.size()-1;
while(left<right) {
int curArea = min(height[left],height[right])*(right-left);
mxArea = max(curArea,mxArea);
if(height[left]<height[right])
left++;
else if(height[left]>height[right])
right--;
else {
left++;
right--;
}
}
return mxArea;
}
};