对“接雨水-二维”问题的思考
类比坐标轴上的接雨水,记一个单元格的水位高度为h
(如果无水,则为该单元格的高度),可以知道,h
为该单元格到所有边缘格的所有路径中,最大高度的最小值。(我是这样理解的:考虑水沿任何路径向边缘流动,这条路径的贡献就是其最高位置。所有路径最大高度组成了一个圈,由木桶效应可知,其中的最小值就是最终水位)。
于是对每个单元格,有\(h[i][j]=max(ar[i][j],min(上下左右的h))\)。初始化边缘格的状态,然后使用Bellman-Ford算法跑一遍。由于最长的更新路线可能达到n*m(螺旋线),所以最外层跑n*m次。时间复杂度为\(O(n^2m^2)\)
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
cin>>ar[i][j];
if(i==1 || i==n || j==m || j==1)
dp[i][j]=ar[i][j];
else
dp[i][j]=1e9;
}
}
for(int k=1;k<=n*m;++k)
{
for(int i=2;i<n;++i)
{
for(int j=2;j<m;++j)
{
dp[i][j]=min(dp[i][j],max(ar[i][j],min(min(dp[i-1][j],dp[i+1][j]),min(dp[i][j-1],dp[i][j+1]))));
}
}
}
如何快速求出“一个单元格到所有边缘格的所有路径中,最大点权的最小值”?我在分析Dijkstra算法时阐述过,Dijkstra算法是适于解决两点间所有路径的最大点权最小值问题的。但是如果对每一个单元格跑一次Dijkstra,显然不比上一种方法高效多少。
考虑一次求出所有单元格的h值的方法。我们用一个超级汇点连接所有边缘格,然后反过来作为源点,求出它到所有单元格的路径最大点权最小值,这样就顺利地解决了问题,时间复杂度为\(O(nmlog(nm))\)。
priority_queue<dat> pq;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
cin>>ar[i][j];
if(i==1 || i==n || j==m || j==1)
{
dp[i][j]=ar[i][j];
pq.push(dat(i,j,dp[i][j]));
}
else
dp[i][j]=1e9;
}
}
while(!pq.empty())
{
dat t=pq.top();
pq.pop();
if(vis[t.x][t.y]) continue;
vis[t.x][t.y]=1;
for(int i=0;i<4;++i)
{
int tx=t.x+dir[i][0],ty=t.y+dir[i][1];
if(tx<1 || tx>n || ty<1 || ty>m || vis[tx][ty]) continue;
dp[tx][ty]=max(min(dp[tx][ty],dp[t.x][t.y]),ar[tx][ty]);
pq.push(dat(tx,ty,dp[tx][ty]));
}
}