1. 程式人生 > >LeetCode 11 - 盛最多水的容器 - [雙指針暴力]

LeetCode 11 - 盛最多水的容器 - [雙指針暴力]

turn 怎麽 rip () 明顯 right 有一個 get 目前

題目鏈接:https://leetcode-cn.com/problems/container-with-most-water/description/

給定 n 個非負整數 $a_1,a_2,\cdots,a_{n-1},a_n$,每個數代表坐標中的一個點 $(i, a_i)$。在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 $(i, ai_)$ 和 $(i, 0)$。

找出其中的兩條線,使得它們與 $x$ 軸共同構成的容器可以容納最多的水。

說明:你不能傾斜容器,且 $n$ 的值至少為 2。

示例:

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

(引用來自官網上的)題解:

這種方法背後的思路在於,兩線段之間形成的區域總是會受到其中較短那條長度的限制。此外,兩線段距離越遠,得到的面積就越大;

我們在由線段長度構成的數組中使用兩個指針,一個放在開始,一個置於末尾;

此外,我們會使用變量 maxarea 來持續存儲到目前為止所獲得的最大面積;

在每一步中,我們會找出指針所指向的兩條線段形成的區域,更新 maxarea,並將指向較短線段的指針向較長線段那端移動一步。

說實話,其實算法過程非常簡單,實現起來也就個位數行的代碼,但是,怎麽證明這樣的算法是正確的呢?

證明(以下參考https://segmentfault.com/a/1190000016654619):

假設最優解的容器的兩個邊界在位置 $L$ 和 $R$,高度為 $h(L)$ 和 $h(R)$,那麽根據算法,由於每次左右指針中只有一個能移動一步,那麽必然有一個先到達兩個邊界中的一個,

不妨假設是左指針先走到 $L$,即 $l=L$;那麽此時右指針必然還沒有走到 $R$,即 $r > R$;

那麽,只要證明如下假設:接下來的每一步都只能是右指針左移一格,左指針始終不能動。就意味著我們的算法在枚舉過程中,必然會在某一步走到最優解,從而使得算法得到的答案正確;

采用反證法:

若上述假設不成立,則意味著如下假設成立:必然存在某一步,是左指針右移一格,而右指針沒有動;

則根據以上描述可知,在這一步的時候:$l=L<R<r$ 且 $h(l) = h(L) < h(r)$,由此可知此時邊界為 $l$ 和 $r$ 的容器的容量 $S$ 為

$S = h\left( l \right) \times \left( {r - l} \right) = h\left( L \right) \times \left( {r - l} \right)$

然而,此時我們根據 $r > R \Rightarrow r - l > R - l = R - L$ 又有

$S = h\left( L \right) \times \left( {r - l} \right) > \min \left( {h\left( L \right),h\left( R \right)} \right) \times \left( {R - L} \right)$

很明顯,$\min \left( {h\left( L \right),h\left( R \right)} \right) \times \left( {R - L} \right)$ 已經是最大的容積了,顯然不應當存在不這還大的容器,

因此,證明了假設:當左指針先走到 $L$ 時,接下來的每一步都只能是右指針左移一格,左指針始終不能動;

類似地,也可以證明,當右指針先走到 $R$ 時,接下來的每一步都只能是左指針右移一格,右指針始終不能動;

證畢。

AC代碼:

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

LeetCode 11 - 盛最多水的容器 - [雙指針暴力]