码迷,mamicode.com
首页 > 移动开发 > 详细

[C++]LeetCode: 131 Trapping Rain Water (双边扫描)

时间:2015-02-04 13:08:10      阅读:256      评论:0      收藏:0      [点我收藏+]

标签:array   dynamic programming   greedy   two pointers   leetcode   

题目:

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example, 
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

技术分享

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Answer 1:

思路:我们分析下这道题目,我们要得到每一个bar的储水量,需要知道它的左右最大高度,用比较小的高度(瓶颈,短板)和当前bar的高度做差得到储水量。第i块地方的存水量 = min(第i块左边最高的bar高度, 第i块右边最高的bar的高度) - 第i块地方bar的高度. 根据这个思路,我们需要进行两次扫描,分别得到每一个bar的左右最大高度。第一次扫描,从左往右,维护对于一个bar左边最大的高度是多少,存入数组对应元素;第二次扫描,从右往左,我们维护右边最大高度;然后同时,比较leftmax[i]和rightmax[i],取较小值为瓶颈存入数组对应元素。然后与当前bar做差,将大于0的值存入存水量。这样两次扫描后,就能得到每一个bar能承受的最大水量,从而累加得到结果。

复杂度:O(N), 空间需要一个长度为n的数组,复杂度也是O(N)

Attention:

1.max / min是c++的关键字,不能用来做变量名。

2.leftmax[0] = 0 ,左边是没有凭栏储水的。要先对数组赋值,再计算当前最大值(将当前bar的高度考虑进去)

<span style="font-size:14px;">//记录每一个bar左边最大高度
        for(int i = 0; i < n; i++)
        {
            container[i] = maxheight;
            maxheight = max(maxheight, A[i]);
        }</span>
3. 计算rightmax时,要重置maxheight.

<span style="font-size:14px;">maxheight = 0;</span>
AC Code:

<span style="font-size:14px;">class Solution {
public:
    int trap(int A[], int n) {
        int maxheight = 0;
        int ret = 0;
        vector<int> container(n, 0);
        
        //记录每一个bar左边最大高度
        for(int i = 0; i < n; i++)
        {
            container[i] = maxheight;
            maxheight = max(maxheight, A[i]);
        }
        
        maxheight = 0;
        //记录每一个bar右边最大高度,并找到瓶颈,统计存水量
        for(int i = n - 1; i >= 0; i--)
        {
            container[i] = min(maxheight, container[i]);
            maxheight = max(maxheight, A[i]);
            ret += container[i] - A[i] > 0 ? container[i] - A[i] : 0;
        }
        return ret;
    }
};</span>

这个方法是一种常见技巧,从两边各扫描一次得到我们需要维护的变量,通常适用于当前元素需要两边元素来决定的问题,类似的题目还有candy.


Answer 2: 优化算法

思路:我们还是使用两个指针a和b, 每一次循环,我们更新左边最大的高度从A[0,1,...a],和右侧最大的高度从A[b, b+1, ...n-1],如果(leftmax < rightmax),那么至少leftmax - A[a]的水量可以被存进总量,而不用考虑【a,b】之间的情况,因为我们知道在右侧有一个更高的屏障(rightmax > leftmax)。同时,我们也知道,只能在a处只能存储最多leftmax - A[a]的水量,因为leftmax是左边最大的高度。所以,我们得到这样的结论,在坐标a处,我们恰能存储的水量就是leftmax - A[a]. 我们可以用同样的逻辑应用到当leftmax > rightmax。每一次循环,我们都对应的移动a或者b. 最后两者相遇时,循环结束。优化的算法,我们只需要一次扫描即可。

复杂度:O(N), 空间复杂度O(1)

AC Code:

class Solution {
public:
    int trap(int A[], int n) {
        int sum = 0;
        int a = 0;
        int b = n - 1;
        int leftmax = 0;
        int rightmax = 0;
        
        while(a <= b)
        {
            leftmax = max(leftmax, A[a]); 
            rightmax = max(rightmax, A[b]);
            if(leftmax < rightmax)
            {
                sum += leftmax - A[a];
                a++;
            }
            else
            {
                sum += rightmax - A[b];
                b--;
            }
        }
        return sum;
    }
};

优化的方法,代码更加简洁,不过理解起来稍微有点难,需要对问题理解很清晰,不仅使用了动态规划,同时还利用了些贪心的思想,这个算法每个元素只被访问一次。这种两边往中间夹逼的方法也比较常用,它的核心思路就是向中间夹逼时,能确定接下来移动一侧窗口不可能使结果变得更好(不可能获得更大储水量),所以每次都能确定移动一侧指针,直到相遇为止。用到一些贪心的方法,如Two Sum, Container With Most Water.



[C++]LeetCode: 131 Trapping Rain Water (双边扫描)

标签:array   dynamic programming   greedy   two pointers   leetcode   

原文地址:http://blog.csdn.net/cinderella_niu/article/details/43482897

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!