42. Receiving rainwater (monotonic stack/two-way record prefix/double pointer) 407. Receiving rainwater 2 (priority queue+bfs)

42. catch rain

When I did this question for the first time, I remembered that it was the solution of maintaining a monotonic queue. Let’s take a look now. Okay, to maintain a monotonic queue. While recording the height, we must also record the width of this height. , So a structure is used, and then because the queue is a first-in-last-out form, but it also needs to access the head of the queue, it is more appropriate to use deque for comprehensive consideration.

For the specific difference between queue and deque, see  https://zhuanlan.zhihu.com/p/77981148

class Solution {
public:
    struct node
    {
        int h,x;
        node(int h,int x):h(h),x(x){}
    };
    int trap(vector<int>& height) {
        int n=height.size();
        if (n==0) return 0;
        deque<node> dq;
        node w(height[0],1);
        dq.push_back(w);
        int ans=0;

        for (int i=1;i<n;i++)
        {
            if (height[i]>=dq.front().h)
            {
                int hnow=dq.front().h;
            
                while (!dq.empty())
                {
                    ans+=(hnow-dq.back().h)*dq.back().x;
                    dq.pop_back();
                }

                dq.push_back(node(height[i],1));
            }
            else
            {
                int hnow=height[i];
                int xnow=1;
            
                while (height[i]>=dq.back().h)
                {
                    ans+=(hnow-dq.back().h)*dq.back().x;
                    xnow+=dq.back().x;
                    dq.pop_back();
                }

                dq.push_back(node(height[i],xnow));
            }
        }
        return ans;
    }
};

In addition, it seems that there is a two-way recording method. Let's come to Kangkang, that is, the way to record the largest prefix and the largest suffix is ​​the same as the idea of ​​recording prefix and suffix sum. That is, the original intention of the title is that for each element in the array, we find the highest position that the water can reach after rain, which is equal to the smaller value of the maximum height on both sides minus the current height value, then

algorithm

Find the highest bar height left_max from the subscript i to the leftmost end in the array.
Find the highest bar height right_max from the subscript i to the right end in the array.
Scan the array height and update the answer:
accumulate min(max_left[i],max_right[i])−height[i] to ans

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

};

But this costs O(2n) space complexity, and deque only needs O(n), but if you encounter this question during the interview, it is recommended to use this two-way recording method, which is easier to understand and easier to code.

Okay, I found the question that records the product of prefixes and suffixes. 238. Let    's    compare and record the product of arrays other than itself .

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n=nums.size();
        vector<int> ans(n);
        vector<int> left_mul(n),right_mul(n);
        left_mul[0]=1;
        for (int i=1;i<n;++i)
        left_mul[i]=nums[i-1]*left_mul[i-1];
        right_mul[n-1]=1;
        for (int i=n-2;i>=0;--i)
        right_mul[i]=nums[i+1]*right_mul[i+1];

        for (int i=0;i<n;i++)
        ans[i]=left_mul[i]*right_mul[i];

        return ans;

    }
};

There is also a double pointer algorithm, which is very clever.

Theorem 1: At a certain position i, the amount of water it can store depends on the smaller of the maximum values ​​on the left and right sides of it.

Theorem 2: When we process from left to right to the left subscript, the maximum value left_max on the left is credible to it, but right_max is not credible to it.

Theorem 3: When we process from right to left to the right subscript, the maximum value of right_max on the right is credible to it, but left_max is not credible to it.

For position left, the maximum value on the left side must be left_max, and the maximum value on the right side is "greater than or equal" to right_max. At this time, if it is left_max<right_maxtrue, then it knows how much water it can store. Whether there will be a larger right_max on the right in the future, it will not affect this result. So when left_max<right_max, we hope to deal with the left index, on the contrary, we want to deal with right subscript.

class Solution {
public:
    int trap(vector<int>& height)
    {
        int left = 0, right = height.size() - 1;
        int ans = 0;
        int left_max = 0, right_max = 0;
        while (left <= right) {
            if (left_max < right_max) {
                height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
                ++left;
            }
            else {
                height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
                --right;
            }
        }
        return ans;
    }

};

Summary: In fact, my initial writing method is a bit long. In fact, it can be solved by monotonous stack. There is no need to judge the element at the bottom of the stack. You only need to compare it with the element at the top of the stack once, and then save the subscript, because there is no way to store the height. Record the subscript width information, but if the subscript i is recorded, the height information can be obtained by height[i], and the width information can be obtained by subtracting the subscript. (Note that the subtraction here is the subtraction of top()-1, that is, ab in the stack is currently c, and height[b]<heigth[c] then dis=c-a+1; this processing can solve the current block The problem of recording the width information alone is still more intuitive to calculate by subtracting the larger one on both sides of the groove from the smaller one in the middle).

407. catch rain 2

It can be seen from Rainwater 1 that it must be a pit to accumulate water. The maintenance is one-dimensional, but here is to maintain the two-dimensional. Originally, both sides need to be maintained, but now a circle is to be maintained . This is the first Difficulty, the second difficulty is that it is difficult to think of how to maintain it. Here, we use the priority queue to find the shortest board in the circle , and gradually pour water inward. The magic is that the original circle will not be destroyed if it gradually moves inward. The nature of the circle is equivalent to watering in four directions, and the board will be rebuilt. The template used in this question is the template of the bfs that walks the maze, and it changes from queue to priority_queue, so it seems to be an ordinary priority queue +bfs up.

class Solution {
private:
    int foot[4][2]={-1,0,1,0,0,-1,0,1};

    struct node
    {
        int x,y,h;
        node(int x,int y,int h):x(x),y(y),h(h) {}
        friend bool operator < (node a,node b)
        {
            return a.h>b.h;
        }
    };
public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        if (heightMap.size()==0 || heightMap[0].size()==0) return 0;
        int n=heightMap.size(),m=heightMap[0].size();
        priority_queue<node> q;
        bool vis[n+10][m+10];
        memset(vis,0,sizeof(vis));
        vector<vector<int>> hm(n+10,vector<int>(m+10,0));
        for (int i=0;i<=n+1;i++)
        for (int j=0;j<=m+1;j++)
        {
            if (i==0 || i==n+1 || j==0 || j==m+1)
                vis[i][j]=true;
            else
            {
                hm[i][j]=heightMap[i-1][j-1];
                if (i==1 || i==n || j==1 || j==m)
                {
                    q.push({i,j,hm[i][j]});
                    vis[i][j]=true;
                }
            }
        }
        int ans=0;
        while (!q.empty())
        {
            node w=q.top();
            q.pop();
            for (int i=0;i<4;i++)
            {
                int x=w.x+foot[i][0];
                int y=w.y+foot[i][1];
                if (!vis[x][y])
                {
                    if (hm[x][y]>=w.h)
                    {
                        q.push({x,y,hm[x][y]});
                        vis[x][y]=true;
                    }
                    else
                    {
                        ans+=w.h-hm[x][y];
                        q.push({x,y,w.h});
                        vis[x][y]=true;
                    }
                }
            }
        }
        return ans;
    }
};

note:

1. Remember to enlarge the array vis and hm

2.q.push({i,j,hm[i][j]}); is a way of writing, q.push(node(i,j,hm[i][j])); should also be a Writing.

Then, because I moved the original matrix from [0..n-1][0..m-1] to [1..n][1..m], it was a little troublesome to write. If I didn’t move it, I might write more Simple, see below.

class Solution {
private:
    int foot[4][2]={-1,0,1,0,0,-1,0,1};

    struct node
    {
        int x,y,h;
        node(int x,int y,int h):x(x),y(y),h(h) {}
        friend bool operator < (node a,node b)
        {
            return a.h>b.h;
        }
    };
public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        if (heightMap.size()==0 || heightMap[0].size()==0) return 0;
        int n=heightMap.size(),m=heightMap[0].size();
        priority_queue<node> q;
        bool vis[n+10][m+10];
        memset(vis,0,sizeof(vis));

        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++)
            if (i==0 || i==n-1 || j==0 || j==m-1)
            {
                q.push(node(i,j,heightMap[i][j]));
                vis[i][j]=true;
            }
        int ans=0;
        while (!q.empty())
        {
            node w=q.top();
            q.pop();
            for (int i=0;i<4;i++)
            {
                int x=w.x+foot[i][0];
                int y=w.y+foot[i][1];
                if (x>=0 && x<n && y>=0 && y<m && !vis[x][y])
                {
                    if (heightMap[x][y]<w.h)
                        ans+=w.h-heightMap[x][y];
                    q.push(node(x,y,max(w.h,heightMap[x][y])));
                    vis[x][y]=true;
                }
            }
        }
        return ans;
    }
};

This idea in the problem solution feels clearer, you can refer to:

https://leetcode-cn.com/problems/trapping-rain-water-ii/solution/407-jie-yu-shui-zui-xiao-dui-cjie-fa-by-he-qian-3/

 

 

Guess you like

Origin blog.csdn.net/hbhhhxs/article/details/107805446