Leetcode #407 Trapping Rain Water II
问题描述
给出一个n*m的矩阵表示一块区域的地形,每个格子的数值表示这个格子所代表的地势高度。
问对于这样的一个矩阵,最多能储备多少的水?
算法分析
- 这题其实看题目就知道,这是之前一维的储水问题的升级版,从一维变成了二维。
- 但是,看了我博客中关于一维的题解报告,并希望能拓展到这道题中,是完全不可行的。这也是这题卡了我非常非常久的原因。
- 因此,我们不妨先回归到一维,我们换个思路。
- 在一维的算法中,我是用了一个栈维护某一个位置之前的比这个位置高的高度和下标是多少。
- 但是放眼到整道题来想想,某一个位置能存多少水,取决于该位置左边最高的地形和右边最高的地形。即所谓的短板效应。 ans[i]=min(maxHeightLeft(i),maxHeightRight(i))−height[i]
- 因此,我们需要两个指针,一个从左边枚举,另一个从右边枚举,每次选择左边或者右边指针中指向的高度较低的点拓展,如果该点比其之前访问的相邻的点高,则更新这边的最大值,否则该点的储水量就是上面公式所示内容。
- 根据这个一维的思路,我们拓展到二维中。一维是从两边夹中间,从而确定出每一个格子的储水量,那二维的不妨也这样。我们将矩阵看成一个桶,从四周开始往中间搜索,不断寻找和更新这个桶的边缘最短板,从而确定每个格子的储水量。
- step1:一开始先将矩阵周围一圈的高度丢入一个最小堆中。将桶的最短板设定为堆头。记最短板高度为H。
- step2:取出堆头(即当前搜到的最小高度)作为当前节点(now)进行广搜拓展不在堆中的点,并将其丢进最小堆中。设拓展出的结点为v。
- step3:如果height[v] < H,则ans+=H-height[v]
- step4:如果从now拓展出来的所有v,都有height[v] >= H,则更新桶的最短板,H=堆头。
- 回到step2,直到整个矩阵被完全访问。
- 时间复杂度:O(nm*log(nm))
代码
class Solution
{
public:
int trapRainWater(vector<vector<int> >& heightMap)
{
int n=heightMap.size();
if (n==0) return 0;
int m=heightMap[0].size();
if (m==0) return 0;
int ans=0;
priority_queue<point> heap;
vector<vector<bool> > bj(n,vector<bool> (m,false));
//桶的边缘进堆
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)
{
heap.push(point(i,j,heightMap[i][j]));
bj[i][j]=true;
}
//以堆为数据结构,广搜
int maxheight=-1;
while (!heap.empty())
{
point now=heap.top();
//maxheight采用这种更新方式,可以舍去我算法描述中的两种情况的判断
maxheight=max(maxheight,now.height);
heap.pop();
for (int i=0;i<4;i++)
{
int x=now.x+xi[i];
int y=now.y+yi[i];
if (x>=0 && x<n && y>=0 && y<m && bj[x][y]==false)
{
bj[x][y]=true;
heap.push(point(x,y,heightMap[x][y]));
if (heightMap[x][y]<maxheight) ans+=maxheight-heightMap[x][y];
}
}
}
return ans;
}
private:
struct point
{
int x,y,height;
point(int xx,int yy,int h):x(xx),y(yy),height(h) {}
friend bool operator < (const point &a,const point &b)
{
return a.height>b.height;
}
};
const int xi[4]={-1,0,1,0};
const int yi[4]={0,1,0,-1};
};