力扣 #11 盛最多水的容器(C++)

题目如下:官网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;
    }
};

猜你喜欢

转载自blog.csdn.net/Raymond_YP/article/details/108763596