每日一题

盛最多水的容器

Posted by zhaiyunfan on April 18, 2020

题面

11. 盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

即为求最大可围成的取区域

输入:[1,8,6,2,5,4,8,3,7]
输出:49

题解

我的题解

class Solution {
public:
    int maxArea(vector<int>& height)
    {
        int left = 0;
        int right = height.size() - 1;
        int v = ((right - left) * min(height[left], height[right]));
        while (left < right)
        {
            if (height[left] < height[right])
            {
                ++left;
            }
            else
            {
                --right;
            }
            v = max(v, (right - left) * min(height[left], height[right]));
        }
        return v;
    }
};

“思路”

算法流程: 设置双指针 ii,jj 分别位于容器壁两端,根据规则移动指针(后续说明),并且更新面积最大值 res,直到 i == j 时返回 res。

指针移动规则与证明: 每次选定围成水槽两板高度 h[i],h[j] 中的短板,向中间收窄1格。以下证明:

设每一状态下水槽面积为:

S(i, j)S(i,j),(0 <= i < j < n)(0<=i<j<n)

由于水槽的实际高度由两板中的短板决定,则可得面积公式:

S(i, j) = min(h[i], h[j]) × (j - i)S(i,j)=min(h[i],h[j])×(ji)

在每一个状态下,无论长板或短板收窄1格,都会导致水槽底边宽度−1:

若向内移动短板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 可能变大,因此水槽面积 S(i, j)S(i,j) 可能增大。

若向内移动长板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 不变或变小,下个水槽的面积一定小于当前水槽面积。

因此,向内收窄短板可以获取面积最大值。换个角度理解:

若不指定移动规则,所有移动出现的 S(i, j)S(i,j) 的状态数为 C(n, 2)C(n,2),即暴力枚举出所有状态。

在状态 S(i, j)S(i,j) 下向内移动短板至 S(i + 1, j)S(i+1,j)(假设 h[i] < h[j]h[i]<h[j] ),则相当于消去了 {S(i, j - 1), S(i, j - 2), … , S(i, i + 1)}S(i,j−1),S(i,j−2),…,S(i,i+1) 状态集合。而所有消去状态的面积一定 <= S(i, j)<=S(i,j):

短板高度:相比 S(i, j)S(i,j) 相同或更短(<= h[i]<=h[i]);

底边宽度:相比 S(i, j)S(i,j) 更短。

因此所有消去的状态的面积都 < S(i, j)<S(i,j)。通俗的讲,我们每次向内移动短板,所有的消去状态都不会导致丢失面积最大值 。

复杂度分析:

时间复杂度 O(N)O(N),双指针遍历一次底边宽度 NN 。

空间复杂度 O(1)O(1),指针使用常数额外空间。