难度中等1494
给你 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。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
题目解析:
本题有两种解法,一种是暴力法,每个面积都算一遍,时间复杂度为O(n!),时间复杂度太高。另一种为双指针法,那么为什么可以使用双指针法。
指针移动规则与证明: 每次选定围成水槽两板高度 h[i]h[i],h[j]h[j] 中的短板,向中间收窄 11 格。以下证明:
(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])×(j−i)。
(2)在每一个状态下,无论长板或短板收窄 11 格,都会导致水槽 底边宽度 -1−1:
1)若向内移动短板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 可能变大,因此水槽面积 S(i, j)S(i,j) 可能增大。
2)若向内移动长板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 不变或变小,下个水槽的面积一定小于当前水槽面积。
(3)因此,向内收窄短板可以获取面积最大值。换个角度理解:
1)若不指定移动规则,所有移动出现的 S(i, j)S(i,j) 的状态数为 C(n, 2)C(n,2),即暴力枚举出所有状态。
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) 更短。
3)因此所有消去的状态的面积都 < S(i, j)<S(i,j)。通俗的讲,我们每次向内移动短板,所有的消去状态都不会导致丢失面积最大值 。
参考代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <list>
#include <unordered_map>
#include <cstring>
#include <map>
#include <stdexcept>
using namespace std;
class Solution {
public:
int maxArea(vector<int>& height) {
if (height.empty())
return 0;
int left = 0;
int right = height.size() - 1;
int max_area = 0;
//使用双指针法,两个指针的移动,主要考虑当前值的大小
while (left < right)
{
if ( min(height[left], height[right]) * (right - left) > max_area)
{
//面积的计算公式:S = min(h[i], h[j]) × (j - i)
max_area = min(height[left], height[right]) * (right - left);
}
//如果左边小于右边,那么左指针向右移动
if (height[left] < height[right])
{
++left;
}
else if (height[left] > height[right])//如果左边大于右边,那么右指针向左移动
{
--right;
}
else if (height[left] == height[right])//如果左边等于右边
{
//比较两个指针的下一个值,谁大就谁移动
if ((left + 1 < right - 1) && height[left + 1] > height[right - 1])
++left;
else if ((left + 1 <= right - 1) && height[left + 1] <= height[right - 1])
--right;
else if (left + 1 > right - 1) //考虑一下边界情况,如果两个指针指过头了就走出循环
break;
}
}
return max_area;
}
};
int main()
{
Solution solution;
vector<int> my_array;
int arr[] = { 1, 2, 1 };
my_array.assign(arr, arr + 3);
cout << solution.maxArea(my_array) << endl;
system("pause");
return 0;
}