对“接雨水-二维”问题的思考

类比坐标轴上的接雨水,记一个单元格的水位高度为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]));
    }
}
posted @ 2022-10-04 22:30  ice_dragon_grass  阅读(67)  评论(0)    收藏  举报