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.
给定n条线段,每条线段的端点为(i,0)和(i,a[i]),现在需要在这n条线段中选择两条并与x轴组成一个水桶,求最大容量的水桶的容量(面积)。
暴力的方法就是直接枚举两条线段,找最大面积,这样复杂度是O(n^2)的。(没想到leetOJ居然还有稍强的数据,感觉是故意卡死这个复杂度的方法的。。)
我换了一个思路后,想到由于最后的最大面积一定是以某一个线段为桶的一侧(可能是左侧或者右侧)并且刚好淹没这条线段,那我们只需要枚举每条线段,然后找出这条线段左侧(或者右侧)比它高的最左(最右)的一个线段的位置,然后计算面积,并记录最大面积即可。
找到左侧比当前线段要高的最左侧线段的方法,可以直接将每个线段先按高度从大到小,再按坐标从小到大排序,然后扫描数组,记录 到当前的最小坐标,由于后扫描到的线段高度一定比先扫描到的高度小,所以只需要记录前面的坐标的最小值,便是当前线段的最左侧线段的坐标。时间复杂度O(nlogn)空间O(n)。
官网给出的标准方法,由于智力有限并不能想到,但是对其正确性还是可以给出证明。方法如下:
令指针i,j指向数组的开头和结尾,然后每次选取height[i]和height[j]较小的那个指针,并将指针想中间位置移动以为,并重新计算面积。(时间复杂度O(n),空间复杂度O(1))。
虽然我不能对这个方法有比较好的启发式的思维来想到,但是我至少还是得对其正确性给出证明,我的方法如下:
令DP(i,j)表示区间[i,j]的最大桶的面积,令a(i,j)表示线段i,j和x轴的面积( min(height[i], height[j]) * (j - i) )。那么最后求解的答案就是DP(0, n-1),边界为DP(i,i)=0。那么此题的DP方法就可以是:
DP(i, j) = max{ DP(i+1, j), DP(i, j-1), a(i, j) }
此时我们令height[i] <= height[j],那么只要满足DP(i+1, j) >= DP(i, j-1),上式可以转化为DP(i, j) = max{DP(i+1, j), a(i, j)},即上面的思路。所以问题转化为: 当height[i] <= height[j]时,证明DP(i+1, j) >= DP(i, j - 1)恒成立。
由于DP(m, n)代表一系列a(i, j)(i<=j)的最大值那么对DP(i+1, j)和DP(i, j-1)经过拆分后可以得到:
DP(i+1,j) = max{DP(i+1, j-1), a(i+1,j), a(i+2,j), ... a(j-1,j), a(j, j)}
DP(i, j-1) = max{DP(i+1, j-1), a(i,i), a(i,i+1), a(i,i+2), ... a(i,j-1) }
由于a(i+x, j) >= a(i, i+x)恒成立
所以得证。