柱状图储水问题

方法一:某bar上的储水 = min(它左侧(含)最高的bar高,它右侧(含)最高的bar高) - 它自己的bar高

int trap(vector<int>& height) {
    if (height.empty()) return 0;
    // 某bar上的储水 = min(它左侧(含)最高的bar高,它右侧(含)最高的bar高) - 它自己的bar高
    const int N = height.size();
    vector<int> leftMax(N, 0);
    leftMax[0] = height[0];
    for (int i = 1; i < N; i++) {
        leftMax[i] = max(height[i], leftMax[i-1]);
    }
    vector<int> rightMax(N, 0);
    rightMax[N-1] = height[N-1];
    for (int i = N - 2; i >= 0; i--) {
        rightMax[i] = max(height[i], rightMax[i+1]);
    }

    int ans = 0;
    for (int i = 0; i < N; i++) {
        ans += min(leftMax[i], rightMax[i]) - height[i];
    }
    return ans;
}

方法二:"波谷"的左右都比它高,贡献一个水层

int trap(vector<int>& height) {
    // 用栈找波谷
    int ans = 0;
    stack<int> stk;
    for (int i = 0; i < height.size(); i++) {
        while (!stk.empty() && height[i] > height[stk.top()]) {
            int valley = stk.top(); stk.pop();
            if (stk.empty()) break;
            int left = stk.top();
            // "波谷"的右i和左left都比它高,贡献一个水层
            int w = i - left - 1;
            int h = min(height[i], height[left]) - height[valley];
            ans += w * h;
        }
        stk.push(i);
    }
    return ans;
}

二维柱状图储水问题

含水高度最小的先出队处理,看它的邻居。若邻居的含水高度更小,该邻居柱上可储水,储水量为高度差。

int trapRainWater(vector<vector<int>>& heightMap) {
    // 可视化解释:https://www.youtube.com/watch?v=cJayBq38VYw
    // 用优先队列的遍历算法
    if (heightMap.empty()) return 0;
    const int R = heightMap.size(), C = heightMap[0].size();
    // 队列元素int[3]: { row, col, heightWithWater }
    auto cmp = [](vector<int> &a, vector<int> &b) {
        return a[2] > b[2]; // 高度最小的先出队处理
    };
    priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> pq(cmp);
    vector<vector<bool>> visited(R, vector<bool>(C, false));
    // 四条边先入队
    for (int r = 0; r < R; r++) {
        for (int c = 0; c < C; c++) {
            if (r == 0 || r == R - 1 || c == 0 || c == C - 1) {
                pq.push({r, c, heightMap[r][c]});
                visited[r][c] = true;
            }
        }
    }

    int ans = 0;
    vector<vector<int>> dirs = { {-1, 0}, {0,1}, {1,0}, {0,-1} };
    while (!pq.empty()) {
        auto elem = pq.top();  pq.pop();
        int r = elem[0], c = elem[1], heightWithWater = elem[2];

        for (auto &dir : dirs) {
            int nr = r + dir[0], nc = c + dir[1];
            if (nr < 0 || nr >= R || nc < 0 || nc >= C || visited[nr][nc]) continue;
            // 若邻居的高度更小,该邻居柱上可储水,储水量为高度差
            int diff = heightWithWater - heightMap[nr][nc];
            if (diff > 0) ans += diff;
            pq.push({nr, nc, max(heightWithWater, heightMap[nr][nc])});
            visited[nr][nc] = true;
        }            
    }
    return ans;
}