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.
简单翻译: 有一些竖线,坐标分别是 (i,ai); 问选 其中两条线 作为容器的两侧 ,X轴为底边,最大容量是多少。
如果选择 (i, a)(j,b) 那么容量就是 |i-j| * min(a,b) 。
初级思路,遍历,所有坐标, 复杂度 O(n^2) ,选最大值。 小数据能过,大数据果断超时。
中级思路, 这样的值 有两个性质, 不妨设 (i,a)为左边 (j,b)为右边为最大值,
1.若有坐标( j+k,c),k>0 必然 b>c ,否则(i,a)(j+k, c) 的容积 (k+j-i)*min(a,c) 大于 (j-i)*min(a,b)
即b 比右边的坐标都大
2. 同理, a 比 左边的坐标值都大
所以,可以做预处理,把 比左边的大的坐标,标记出来, 把比右边都大的坐标也标记出来。
例如:如下数组,第一个坐标是(0,5),第二坐标(1,2),最后一个是(9,4);
5 | 2 | 12 | 1 | 5 | 3 | 4 | 11 | 9 | 4 |
1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
第一行是ai, 第二行是左标记,第三行 右标记 ,标记的复杂度是O(n) 。
求解的遍历方式:i 从0 开始 j 从 size()-1 开始, i<j ; i和j 仅取相应的标记值的值。
这是相当于一个较大的剪枝, 大数据能过。
代码如下:
class Solution { public: int maxArea(vector<int> &height) { // Start typing your C/C++ solution below // DO NOT write int main() function int max = 0; vector<int> flag; flag.resize(height.size()); int lmax = 0; for(int i = 0; i < height.size();i++) { if(height[i]<lmax) flag[i]=0; else{ flag[i]=1; lmax = height[i]; } } int rmax = 0; for(int i = height.size()-1; i >= 0;i--) { if(height[i]>=rmax) { flag[i]+=2; rmax = height[i]; } } for(int i = 0 ; i < height.size() ; i++) { if(flag[i]%2==0) continue; for (int j = height.size()-1 ; j >i ; j--) { if( flag[j] <2) continue; int sum = min(height[i],height[j]) * (j- i); if ( max < sum) max =sum; } } return max; } };
得出36,然后是(0,5),(8,9);得出40,之后是(0,5),(7,11)得出35,实际上这已经不需要继续遍历 右边了,
右边标记位是递增的,当9比5大之后,右边能遍历到的数都比5大, 而坐标依次减少,所以得出的容积,只会越来越小。
结论: 抽象的说,在(i,a),(j,b)的情况下, 若 a <b , (i,a)的情况已经遍历完成,j无需移动, i向右移动,i++;
同样 若 b <a , (i, a ) 的 情况要继续遍历,或者说,(j,b)的情况已经求解完成, j--;
复杂度O(n)
上面的代码大数据要600多ms,下面的仅需104ms
Run Status: Accepted!
Program Runtime: 104 milli secs
代码如下:
class Solution { public: int maxArea(vector<int> &height) { // Start typing your C/C++ solution below // DO NOT write int main() function int max = 0; vector<int> flag; flag.resize(height.size()); int lmax = 0; for(int i = 0; i < height.size();i++) { if(height[i]<lmax) flag[i]=0; else{ flag[i]=1; lmax = height[i]; } // cout <<i << "L:"<< flag[i] << endl; } int rmax = 0; for(int i = height.size()-1; i >= 0;i--) { if(height[i]>=rmax) { flag[i]+=2; rmax = height[i]; } } for(int i = 0, j = height.size()-1 ; i < j ;) { if(flag[i]%2==0) { i++; continue; } if (flag[j]<2) { j--; continue; } int sum = min(height[i],height[j]) * (j- i); if ( max < sum) max =sum; if(height[i]<height[j]) { i++; }else{ j--; } } return max; } };