LeetCode Trapping Rain Water

class Solution {
public:
    int trap(int A[], int n) {
        if (n < 1) return 0;
        vector<int> peaks;
        vector<int> peaks_tmp;

        int last_idx = 0;

        bool increasing = true;
        for (int i=1; i<n; i++) {
            if (A[i] < A[last_idx]) {
                if (increasing) {
                    peaks.push_back(last_idx);
                }
                increasing = false;
            } else {
                increasing = true;
            }
            last_idx = i;
        }
        if (increasing) peaks.push_back(n - 1);

        if (peaks.size() < 2) return 0;
        
        bool updated = true;

        while (updated) {
            updated = false;
            peaks_tmp.clear();
            peaks_tmp.push_back(peaks[0]);
            peaks_tmp.push_back(peaks[1]);
            for (int i=2; i<peaks.size(); i++) {
                int tlen = peaks_tmp.size();
                int ai = peaks_tmp[tlen - 2];
                int bi = peaks_tmp[tlen - 1];
                int ci = peaks[i];

                if (A[ai] >= A[bi] && A[ci] >= A[bi]) {
                    peaks_tmp[tlen - 1] = ci;
                    updated = true;
                } else {
                    peaks_tmp.push_back(ci);
                }
            }
            swap(peaks, peaks_tmp);
        }

        int rain = 0;

        for (int i=1; i<peaks.size(); i++) {
            int left = peaks[i - 1];
            int right= peaks[i];

            int h = min(A[left], A[right]);
            int blocks = 0;
            for (int i=left + 1; i<right; i++) blocks += A[i] > h ? h : A[i];
            rain += h * (right - left - 1) - blocks;
        }
        return rain;
    }
};

先求出各个block的峰值,然后雨水肯定落在峰值block之间,对这些峰值block为界限的区间进行合并(对于序列A{4,1,3,1,5},第一个区间为A(0,2),第二个区间为A(2,4),由于A[4] > A[2] 且 A[0] > A[2]所以可以合并为区间A(0, 4)),直到不能继续合并,最终计算这些block区间所能积累的水量。

再稍稍改进一下,合并过程在求峰值时同时进行,额外数组减少到一个

class Solution {
public:
        int trap(int A[], int n) {
        if (n < 1) return 0;
        vector<int> peaks;

        int last_idx = 0;

        bool increasing = true;
        for (int i=1; i<n; i++) {
            if (A[i] < A[last_idx]) {
                if (increasing) {
                    peaks.push_back(last_idx);
                    compact(A, peaks);
                }
                increasing = false;
            } else {
                increasing = true;
            }
            last_idx = i;
        }
        
        if (increasing) peaks.push_back(n - 1);
        compact(A, peaks);
        
        if (peaks.size() < 2) return 0;

        int rain = 0;

        for (int i=1; i<peaks.size(); i++) {
            int left = peaks[i - 1];
            int right= peaks[i];

            int h = min(A[left], A[right]);
            int blocks = 0;
            for (int i=left + 1; i<right; i++) blocks += A[i] > h ? h : A[i];
            rain += h * (right - left - 1) - blocks;
        }
        return rain;
    }

    void compact(int A[], vector<int> &peaks) {
        int len = peaks.size();
        if (len < 3) return;
        bool updated = true;
        while (updated && (len = peaks.size()) >= 3) {
            updated = false;
            int ai = peaks[len - 3];
            int bi = peaks[len - 2];
            int ci = peaks[len - 1];
            if (A[ai] >= A[bi] && A[ci] >= A[bi] ) {
                peaks[len - 2] = ci;
                peaks.pop_back();
                updated = true;
            }
        }
    }
};

另外一种简洁方法参见:http://www.cnblogs.com/zhuli19901106/p/3542216.html,如下

class Solution {
public:
    int trap(int A[], int n) {
        if (n < 2) return 0;
        vector<int> lmax(n, 0);
        vector<int> rmax(n, 0);
        
        int m = A[n-1];
        for (int i=n-2; i >= 0; i--) {
            if (A[i] > m) m = A[i];
            rmax[i] = m;
        }
        
        m = A[0];
        for (int i=1; i<n; i++) {
            if (A[i] > m) m = A[i];
            lmax[i] = m;
        }

        int rain = 0;
        for (int i=0; i<n; i++) {
            int h = min(lmax[i], rmax[i]);
            int v = h - A[i];
            rain += v < 0 ? 0 : v;
            
        }
        return rain;
    }
};

第二轮:

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!

LH数组可以不用存储直接在最后一个循环过程中计算。

 1 class Solution {
 2 public:
 3     int trap(vector<int>& height) {
 4         int len = height.size();
 5         if (len == 0) {
 6             return 0;
 7         }
 8         vector<int> LH(len, 0);
 9         LH[0] = height[0];
10         for (int i=1; i<len; i++) {
11             LH[i] = max(LH[i-1], height[i]);
12         }
13         
14         vector<int> RH(len, 0);
15         RH[len-1] = height[len-1];
16         for (int i=len-2; i>=0; i--) {
17             RH[i] = max(RH[i+1], height[i]);
18         }
19         
20         int water = 0;
21         for (int i=0; i<len; i++) {
22             int ah = min(LH[i], RH[i]);
23             if (ah <= height[i]) {
24                 continue;
25             }
26             water += ah - height[i];
27         }
28         return water;
29     }
30 };

 还有更高效的做法,真是智商碾压啊,短短几行,还不用额外空间,艹

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        int p = 0, q = len - 1;
        int maxh = 0;
        int sum = 0;
        while (p < q) {
            if (height[p] < height[q]) {
                maxh = max(height[p], maxh);
                sum += maxh - height[p];
                p++;
            } else {
                maxh = max(height[q], maxh);
                sum += maxh - height[q];
                q--;
            }
        }
        return sum;
    }
};

 又从discuss中找到一种方法,似乎这种比较通用一些可以解决一些类似的问题:

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        stack<int> pos;
        int sum = 0;
        for (int i = 0; i < len; i++) {
            while (!pos.empty() && height[i] > height[pos.top()]) {
                int base = height[pos.top()];
                pos.pop();
                if (!pos.empty()) {
                    int w = i - pos.top() - 1;
                    int h = min(height[i], height[pos.top()]) - base;
                    sum += w * h;
                }
            }
            pos.push(i);
        }
        return sum;
    }
};

不过相比前一种使用了辅助空间。

原文地址:https://www.cnblogs.com/lailailai/p/3856431.html