1. 程式人生 > >[LeetCode] Trapping Rain Water II 收集雨水之二

[LeetCode] Trapping Rain Water II 收集雨水之二

Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.

Note:
Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000.

Example:

Given the following 3x6 height map:
[
  [1,4,3,1,3,2],
  [3,2,1,3,2,4],
  [2,3,3,2,3,1]
]

Return 4.


The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain.


After the rain, water are trapped between the blocks. The total volume of water trapped is 4.

這道題是之前那道Trapping Rain Water的拓展,由2D變3D了,感覺很叼。但其實解法跟之前的完全不同了,之前那道題由於是二維的,我們可以用雙指標來做,而這道三維的,我們需要用BFS來做,解法思路很巧妙,下面我們就以題目中的例子來進行分析講解,多圖預警,手機流量黨慎入:

首先我們應該能分析出,能裝水的底面肯定不能在邊界上,因為邊界上的點無法封閉,那麼所有邊界上的點都可以加入queue,當作BFS的啟動點,同時我們需要一個二維陣列來標記訪問過的點,訪問過的點我們用紅色來表示,那麼如下圖所示:

我們再想想,怎麼樣可以成功的裝進去水呢,是不是周圍的高度都應該比當前的高度高,形成一個凹槽才能裝水,而且裝水量取決於周圍最小的那個高度,有點像木桶原理的感覺,那麼為了模擬這種方法,我們採用模擬海平面上升的方法來做,我們維護一個海平面高度mx,初始化為最小值,從1開始往上升,那麼我們BFS遍歷的時候就需要從高度最小的格子開始遍歷,那麼我們的queue就不能使用普通隊列了,而是使用優先順序佇列,將高度小的放在隊首,最先取出,這樣我們就可以遍歷高度為1的三個格子,用綠色標記出來了,如下圖所示:

如上圖所示,向周圍BFS搜尋的條件是不能越界,且周圍格子未被訪問,那麼可以看出上面的第一個和最後一個綠格子無法進一步搜尋,只有第一行中間那個綠格子可以搜尋,其周圍有一個灰格子未被訪問過,將其加入優先佇列queue中,然後標記為紅色,如下圖所示:

那麼優先佇列queue中高度為1的格子遍歷完了,此時海平面上升1,變為2,此時我們遍歷優先佇列queue中高度為2的格子,有3個,如下圖綠色標記所示:

我們發現這三個綠格子周圍的格子均已被訪問過了,所以不做任何操作,海平面繼續上升,變為4,遍歷所有高度為4的格子,如下圖綠色標記所示:

由於我們沒有特別宣告高度相同的格子在優先佇列queue中的順序,所以應該是隨機的,其實誰先遍歷到都一樣,對結果沒啥影響,我們就假設第一行的兩個綠格子先遍歷到,那麼那麼周圍各有一個灰格子可以遍歷,這兩個灰格子比海平面低了,可以存水了,把存水量算出來加入結果res中,如下圖所示:

上圖中這兩個遍歷到的藍格子會被加入優先佇列queue中,由於它們的高度小,所以下一次從優先佇列queue中取格子時,它們會被優先遍歷到,那麼左邊的那個藍格子進行BFS搜尋,就會遍歷到其左邊的那個灰格子,由於其高度小於海平面,也可以存水,將存水量算出來加入結果res中,如下圖所示:

等兩個綠格子遍歷結束了,它們會被標記為紅色,藍格子遍歷會先被標記紅色,然後加入優先佇列queue中,由於其周圍格子全變成紅色了,所有不會有任何操作,如下圖所示:

此時所有的格子都標記為紅色了,海平面繼續上升,繼續遍歷完優先佇列queue中的格子,不過已經不會對結果有任何影響了,因為所有的格子都已經訪問過了,此時等迴圈結束後返回res即可,參見程式碼如下:

class Solution {
public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        if (heightMap.empty()) return 0;
        int m = heightMap.size(), n = heightMap[0].size(), res = 0, mx = INT_MIN;
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        vector<vector<int>> dir{{0,-1},{-1,0},{0,1},{1,0}};
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    q.push({heightMap[i][j], i * n + j});
                    visited[i][j] = true;
                }
            }
        }
        while (!q.empty()) {
            auto t = q.top(); q.pop();
            int h = t.first, r = t.second / n, c = t.second % n;
            mx = max(mx, h);
            for (int i = 0; i < dir.size(); ++i) {
                int x = r + dir[i][0], y = c + dir[i][1];
                if (x < 0 || x >= m || y < 0 || y >= n || visited[x][y]) continue;
                visited[x][y] = true;
                if (heightMap[x][y] < mx) res += mx - heightMap[x][y];
                q.push({heightMap[x][y], x * n + y});
            }
        }
        return res;
    }
};

類似題目:

參考資料: