接水问题-盛最多水的容器与接雨水问题

近期在做题的时候,有两道题目比较有启发。

题目来源:LeetCode

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

接雨水 :给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

在做“盛水最多的容器”时,最开始的想法是动态规划,寻找转移方程。但是思索了很久没有想到后一个状态与前一个状态的联系。后来看了解析之后,发现了一种新的看待问题和寻找最优解的思路。

在做这题时,使用双指针,一个指向最左,一个指向最右,维护一个当前的最大值。

思路重点:将两个指针中较小的那一个往中间移动。

背后原理:当前已经有了一个最大值了,可以看做是当前信息条件下的最优。而能够优化这个问题的办法就是将限制当前值的条件进行改变,尝试寻找更优的解法。即,将较小的指针往中间移动。

在其他问题中,也可以通过这种方式寻找思路:找到一个部分最优解,通过优化当前的短板,进而优化整个问题。

class Solution {
    public int getArea(int[] height, int start, int end)
    {
        return Math.min(height[start], height[end])*(end-start);
    }
    public int maxArea(int[] height) {
        int n = height.length;
        int start = 0, end = n-1;
        int max_area = 0;
        while (start!=end)
        {
            int temp_area = getArea(height, start, end);
            max_area = Math.max(max_area, temp_area);
            if (height[start] > height[end])
                end--;
            else
                start++;
        }
        return max_area;
    }
}

 

接雨水问题相对更加复杂。最一开始我的想法整个是错误的:从左向右找“坑”,然后找到一个坑就计算这个坑能接多少,最后加起来。但是这种想法的问题在于没有想到可能有更大的坑包住了这些小坑,根本原因在于局部小坑的边界可能并不是盛水的边界,而可能被包着它的更大的坑漫过。

而这个问题实际的想法是:找到所有柱子里的最大值。这样,最大值这个柱子就不可能被其他的更大的坑漫过。由此,分为了左右两个部分,从左和从右分别向最大值靠近即可。

class Solution {
    
    public int trap(int[] height) {
        if (height == null || height.length <3)
            return 0;
        int pos=0, highest = 0, n = height.length;
        for (int i = 0; i < n; ++i)
        {
            if (height[i] > highest)
            {
                highest = height[i];
                pos = i;
            }
        }
        int temp = height[0], result = 0;
        for (int i = 0; i < pos; ++i)
        {
            if (height[i] < temp)
                result += (temp - height[i]);
            else
                temp = height[i];
        }
        temp = height[n-1];
        for (int i = n-1; i >= pos; --i)
        {
            if (height[i] < temp)
                result += (temp-height[i]);
            else
                temp = height[i];
        }
        return result;
    }
}
View Code

而另一个更近一步的做法是结合前一题的想法,不用第一次先遍历找到最大值,再左右向中间靠近。而可以左右两个指针,谁小向中间移动谁。这样做实际上相当于在这一次遍历的过程中,既找了最大值,又计算了水量。

class Solution {
public:
    int trap(vector<int>& height) {
        int result = 0;
        for (int i = 0, j = height.size() - 1, left_max = 0, right_max = 0; i < j; )
        {
            if (height[i] > left_max) left_max = height[i];
            if (height[j] > right_max) right_max = height[j];
            if (left_max <= right_max) result += left_max - height[i++];
            else result += right_max - height[j--];
        }
        return result;
    }
};

 

转载于:https://www.cnblogs.com/mockingbirdbad/p/10568337.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值