题目如下:官网oj
法一 暴力法
一一枚举容器的左端和右端,两层for循环,O(n2)的时间复杂度,在这里不给出实现代码了。
法二 动态规划
状态的设计:dp[i][j] 代表左端为第i个木条,右端为第j个木条的能容纳的最多的水
状态的转移:dp[i][j]=max(dp[i+1][j],dp[i][j-1],min(height[i],height[j])*(j-i))
考虑dp[i][j]的时候,可以先考虑dp[i+1][j]和dp[i][j-1]之间的最大者,因为我们在考虑dp[i][j]的时候
所有长度小于j-i的dp[a][b]都已经计算了接着考虑i为左边界,j为有边界的情况,取这三者的最大值,
就是dp[i][j]的最终结果,这样可以把dp的区间长度不断扩大,最终区间长度为整个height的size,就得到最终的解dp[0][height.size()-1]
代码如下:
class Solution {
public:
static const int MAXN=100;
int dp[MAXN][MAXN];
int maxArea(vector<int>& height) {
//首先计算区间长度为1的情况
for(int i=0;i<height.size()-1;i++)
dp[i][i+1]=min(height[i],height[i+1]);
//从区间长度为2开始迭代,直到区间长度为height的长度
for(int len=2;len<height.size();len++)
//枚举区间左端点
for(int i=0;i<height.size();i++)
//右端点必须在height的长度之内
if(len+i<height.size()){
dp[i][i+len]=max(dp[i][i+len-1],dp[i+1][i+len]);
dp[i][i+len]=max(dp[i][i+len],min(height[i+len],height[i])*len);
}
return dp[0][height.size()-1];
}
};
本题强行使用动态规划显得笨拙,复杂度同样为O(n2),而且占据了O(n2)的空间复杂度。
法三 双指针法
该做法复杂度仅为O(n)。初始的时候left为1,right为height.size()-1,计算一次水量,如果left的高度小于right,那么left++。否则right–,然后重新计算水量,重复上述过程,直到left>=right。为什么上述做法是正确的呢?考虑如下,如果left的高度小于height的时候,令right–,那么不管新的right高度如何,所装的水依然取决于left的高度,因为水量取决于最低的高度;如果令left++,若新的left更高,则决定水量的木板为left,此时可能得到一个更优的解。具体的证明过程:力扣官方题解
代码如下:
class Solution {
public:
int maxArea(vector<int>& height) {
int left=0,right=height.size()-1;
int res=0;
while(left<right){
res=max(res,(right-left)*min(height[left],height[right]));
if(height[left]<height[right])
left++;
else
right--;
}
return res;
}
};