关于在三维棋盘中查找水量的提示

Tips on finding the volume of water in a 3d chess board

本文关键字:查找 提示 三维      更新时间:2023-10-16

所以我有一个任务,我必须重新创建一个三维棋盘,它是一个RxC网格,每个网格的高度不同。如果棋盘是水密的,有人把水倒在上面,直到它不能再容纳水,它就会容纳一定量的水。如果木板已经容纳了最大体积的水,那么任何多余的水都会从木板的边缘排出,木板周围没有高高的容器。你可以假设棋盘上的正方形是一英寸见方,高度以英寸为单位。

int CalcContainedWater( const int *p_data, int num_columns, int num_rows )

其中,p_data是指向有符号整数的连续二维行主数组的第一个元素的指针。您的功能将根据不同形状和内容的板的参考实现进行测试,以确定其正确性。

请记住,p_data内部的值可以同时包含高度的正值和负值。

例如:

A) 下面的板产生了一个3的安全壳。

1, 1, 1, 1, 1,
1, 0, 0, 0, 1,
1, 1, 1, 1, 1,

B) 下面的板产生了0的包含。

1, 0, 1,
1, 0, 1,
1, 1, 1,

C) 下面的板产生1的包容度。

0, 1, 0,
1, 0, 1,
0, 1, 0,

这就是我目前所拥有的:

    #include "stdafx.h"
    #include <queue>
    #include <vector>
    using namespace std;
enum GridPosition
{
    TOP_LEFT_CORNER,
    TOP_RIGHT_CORNER,
    BOTTOM_LEFT_CORNER,
    BOTTOM_RIGHT_CORNER,
    TOP_ROW,
    BOTTOM_ROW,
    LEFT_COLUMN,
    RIGHT_COLUMN,
    FREE,
};
struct Square
{
    int nHeight;
    int nPos;
    GridPosition gPos;
    bool bIsVisited;
    bool bIsFenced;
    bool bIsFlooding;
    Square(){ nHeight = 0; nPos = 0; gPos = FREE; bIsVisited = false; bIsFenced = false; bIsFlooding = false;};
    ~Square(){};
    Square( int Height, int Pos, GridPosition GridPos, bool Visited, bool Fenced, bool Flooding) 
    { 
        nHeight = Height;
        nPos = Pos;
        gPos = GridPos;
        bIsVisited = Visited;
        bIsFenced = Fenced;
        bIsFlooding = Flooding;
    }
};
template< typename FirstType, typename SecondType >
struct PairComparator 
{
  bool operator()( const pair<FirstType, SecondType>& p1,
    const pair<FirstType, SecondType>& p2 ) const 
    {  
        return p1.second > p2.second;
    }
};
int CalcContainedWater( const int *p_data, int num_columns, int num_rows );
int CalcContainedWater( const int *p_data, int num_columns, int num_rows )
{
    priority_queue<pair<int,int>, vector<pair<int,int>>, PairComparator<int,int>> qPerimeter;
    queue<pair<int,int>> qFlooding;
    vector<Square> vSquareVec(num_columns * num_rows);
    int nTotalContained = 0;
    int nCurrentSqHeight = 0;
    int nCurrWaterLvl = 0;
    int nDepth = 1;
    for( int nRow = 0; nRow < num_rows; ++nRow)
    {
        for( int nColumn = 0; nColumn < num_columns; ++ nColumn)
        {
            int nCurrArrayPoint = nRow * num_columns + nColumn;
            nCurrentSqHeight = p_data[nCurrArrayPoint];
            Square sSquare(nCurrentSqHeight, nCurrArrayPoint, FREE, false,false,false);
            if(nRow == 0  && nColumn == 0)
                sSquare.gPos = TOP_LEFT_CORNER;
            else if(nRow == 0  && nColumn == num_columns - 1)
                sSquare.gPos = TOP_RIGHT_CORNER;
            else if(nRow == num_rows - 1  && nColumn == 0)
                sSquare.gPos = BOTTOM_LEFT_CORNER;
            else if(nRow == num_rows - 1  && nColumn == num_columns - 1)
                sSquare.gPos = BOTTOM_RIGHT_CORNER;
            else if( nRow == 0)
                sSquare.gPos = TOP_ROW;
            else if( nRow == num_rows -1 )
                sSquare.gPos = BOTTOM_ROW;
            else if( nColumn == 0)
                sSquare.gPos = LEFT_COLUMN;
            else if( nColumn == num_columns - 1)
                sSquare.gPos = RIGHT_COLUMN;
            vSquareVec[nCurrArrayPoint] = sSquare;
            if( nRow == 0  || nColumn == 0 ||  
                nColumn == num_columns - 1 || nRow == num_rows -1 )
            {
                sSquare.bIsFenced = true;
                vSquareVec[nCurrArrayPoint] = sSquare;
                pair<int,int> p1(nCurrArrayPoint, nCurrentSqHeight);
                qPerimeter.push(p1);
            }
        }
    }
    nCurrWaterLvl = qPerimeter.top().second;
    while( !qPerimeter.empty() )
    {
        pair<int,int> PerimPos = qPerimeter.top();
        qPerimeter.pop();
        if( !vSquareVec[PerimPos.first].bIsVisited )
        {
            if( vSquareVec[PerimPos.first].nHeight > nCurrWaterLvl )
                nCurrWaterLvl = vSquareVec[PerimPos.first].nHeight;
            vSquareVec[PerimPos.first].bIsFlooding = true;
            qFlooding.push(PerimPos);
            while( !qFlooding.empty() )
            {
                pair<int,int> FloodPos = qFlooding.front();
                qFlooding.pop();
                nDepth = nCurrWaterLvl - vSquareVec[FloodPos.first].nHeight;
                if( nDepth >= 0)
                {
                    vSquareVec[FloodPos.first].bIsVisited = true;
                    pair<int,int> newFloodPos;
                    switch(vSquareVec[FloodPos.first].gPos)
                    {
                    case TOP_LEFT_CORNER:
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case TOP_RIGHT_CORNER:
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case BOTTOM_LEFT_CORNER:
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case BOTTOM_RIGHT_CORNER:
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case TOP_ROW:
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case BOTTOM_ROW:
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case LEFT_COLUMN:
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case RIGHT_COLUMN:
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1 ].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        break;
                    case FREE:
                        if( !vSquareVec[FloodPos.first + 1].bIsVisited && 
                            !vSquareVec[FloodPos.first + 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - 1].bIsVisited && 
                            !vSquareVec[FloodPos.first - 1].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - 1].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - 1].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - 1].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first + num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first + num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first + num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first + num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first + num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        if( !vSquareVec[FloodPos.first - num_rows].bIsVisited && 
                            !vSquareVec[FloodPos.first - num_rows].bIsFlooding)
                        {
                            vSquareVec[FloodPos.first - num_rows].bIsFlooding = true;
                            newFloodPos.first = vSquareVec[FloodPos.first - num_rows].nPos;
                            newFloodPos.second = vSquareVec[FloodPos.first - num_rows].nHeight;
                            qFlooding.push(newFloodPos);
                        }
                        nTotalContained += nDepth;
                        break;
                    }
                }
                else
                {
                    vSquareVec[FloodPos.first].bIsFlooding = false;
                    if( !vSquareVec[FloodPos.first].bIsFenced )
                    {
                        vSquareVec[FloodPos.first].bIsFenced = true;
                        qPerimeter.push(FloodPos);
                    }
                }
            }
        }
    }
    return nTotalContained;
    }

我所找到的只是顶部、底部、左侧和右侧的正方形高度。

目前,我正试图弄清楚如何获得总体积,因为我知道如果正方形的高度较小,水会溢出到正方形上。我看得越多,就越认为它应该递归地完成,但找不到实现它的方法

任何帮助都将不胜感激。不是在寻找答案,只是为了朝着正确的方向推进我需要做的事情。

有趣的问题,有很多不同的解决方案。今天下午我一直在考虑这个问题,我会选择像洪水填充这样的优先队列(也许是最小堆)。让我们称之为fence

您还需要跟踪已访问的项目。最初将所有项目标记为未访问。

首先将网格周边的所有点添加到fence

现在你这样循环:

fence中弹出前面的项目。您已经选择了周界上的一个最低点。

  • 如果该项目已被访问,请将其丢弃并再次循环
  • 如果它没有被访问,只有当它高于当前水位时,它的高度才会成为你的新水位

现在,从该点开始执行洪泛填充。您可以递归地(深度优先)执行此操作,但我将使用无序队列(广度优先)来讨论此问题。让我们把这个队列称为flood。首先将项目推送到flood上。

洪泛处理如下:循环直到flood中没有剩余项目。。。

  • flood弹出项目
  • 如果它已经被访问过,请忽略它并再次循环
  • 如果项目高度小于或等于当前水位,则计算高度差并将其添加到总体积中。将项目标记为已访问,然后将所有未访问的邻居添加到flood
  • 如果项目高度大于当前水位,只需将其添加到fence即可。您需要有一种方法来判断该项目是否已经在fence中——您不想再次添加它。也许你可以扩展你的"已访问"标志来应对这种情况

就是这样。诚然,当我躺在那里感到宿醉和邋遢时,这只是一个思想实验,但我认为这很好。


正如你所要求的。。。一些伪代码。

初始设置:

## Clear flags.  Note I've added a 'flooding' flag
for each item in map
    item.visited <- false     # true means item has had its water depth added
    item.fenced <- false      # true means item is in the fence queue
    item.flooding <- false    # true means item is in the flooding queue
end
## Set up perimeter
for each item on edge of map (top, left, right, bottom)
    push item onto fence
end
waterlevel <- 0
total <- 0

现在算法的主要部分

while fence has items
    item <- pop item from fence
    if item.visited = true then loop again
    ## Update water level
    if item.height > waterlevel then waterlevel = item.height
    ## Flood-fill item using current water level
    push item onto flood
    item.flooding <- true
    while flood has items
        item <- pop item from flood
        depth <- waterlevel - item.height
        if depth >= 0 then
            # Item is at or below water level.  Add its depth to total.
            total <- total + depth
            item.visited <- true
            # Consider all immediate neighbours of item.
            for each neighbour of item
                if neighbour.visited = false then
                    if neighbour.flooding = false then
                        push neighbour onto flood
                        neighbour.flooding <- true
                    end
                end
            end
        else
            # Item is above water
            item.flooding <- false
            if item.fenced = false then
                push item onto fence
                item.fenced <- true
            end
        end
    end
end

这需要的不仅仅是计算体积。

根据您发布的示例,您首先要测试包含,然后担心容器的体积。

我建议确定板中是否存在闭合多边形
如果多边形是闭合的,请确定其面积
用于体积计算的高度将是所有边界墙的最小高度
最后,体积将是最小高度乘以多边形的面积。

研究基于顶点或线段向量确定闭合多边形的网络算法。

这有点暴力,但可能会奏效。

你可以试着从概念上把板子分成几层,例如:

-------------------------
0 | 1 | 1 | 0 | 1 | 1 | 0
1 | 0 |-1 | 1 | 0 | 0 | 1
1 | 1 | 1 | 1 | 1 | 1 | 1
-------------------------

只看最底层。假设-1是底部,则板看起来是这样的:

-------------------------
0 | 0 | 0 | 0 | 0 | 0 | 0
0 | 0 |-1 | 0 | 0 | 0 | 0
0 | 0 | 0 | 0 | 0 | 0 | 0
-------------------------

对于每个正方形,确定是否存在左侧、右侧、顶部和底部都具有较大值的正方形。在这种情况下,我们计算1。

然后移到下一层,填充最后一层的"洞"。

-------------------------
0 | 1 | 1 | 0 | 1 | 1 | 0
1 | 0 | 0 | 1 | 0 | 0 | 1
1 | 1 | 1 | 1 | 1 | 1 | 1
-------------------------

冲洗,重复。在这一层中,我们数了4,总共得到5。

-------------------------
1 | 1 | 1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 1 | 1 | 1 | 1
-------------------------

顶层显然没有,我们完了。

在伪代码中:

for each layer l in layers
   for each square in l
      if there exists a square left, right, top and bottom with higher value
          count the square.

编辑

当然,由于我有严重的问题,当我今天早上醒来时,我首先想到的是这个问题,并立即打破了我的解决方案。

让我们对示例进行一个更改:

-------------------------
0 | 1 | 1 | 0 | 1 | 1 | 0
1 | 0 |-1 | 1 | 0 | 0 | 1
1 | 1 | 1 | 1 | 1 | 0 | 1
-------------------------
                    ^

向外开一个洞。用目前的算法,我们会得到4的解,这显然是错误的。

为了解决这个问题,我们需要实现一个反向跟踪算法。

我们不在左、右、上、下的任何位置寻找更高的值,而是只检查紧邻的正方形。如果有人在同一高度,我们也需要参观那个广场,并再次进行检查。如果我们找到了通往外面的路,那么原始的广场(以及随后访问的所有广场)就会失败。如果我们遇到了死胡同,那么所有访问过的广场都可以计算在内。

通过这种修改,我们将得到3的正确结果。

这里有一段工作代码:基本思想是将板水平划分为多个级别,并确定每个级别可以容纳的音量(复杂性O(x*y*z)):

#include <stdio.h>
#include <memory.h>
// The Cell structure for each level
struct Cell
{
    unsigned char height; // either 0 or 1
    bool visited; // we only visit the cells that have height of 0
};
// The recursive function that visits a cell, accumulate the water volume (or if leaked due to being at the border, reset the values); it also
// looks to its 4 adjacent cells (if valid) recursively.
// Note that the top level function actually attempts to visit *all* the "connected" cells (and mark them as visited, so they will not be visited again)
// From the top level, the cells are thus visited in "chunks" (as long as they are connected)
void VisitCell(Cell* one_level_cells, unsigned short a_w, unsigned short a_h, unsigned short w, unsigned short h, unsigned __int64* sum, bool* leaked)
{
    Cell& cell = one_level_cells[h * a_w + w];
    if (cell.height == 0 && !cell.visited)
    {
        cell.visited = true;
        if (w == 0 || w + 1 == a_w || h == 0 || h + 1 == a_h)
        {
            // I am at the border while I am not guarding the water, the water for this "chunk" is then leaked!
            *leaked = true;
            *sum = 0;
        }
        if (!*leaked)
        {
            // potentially increment the volume, until it's detected leaked at some point
            ++*sum;
        }
        if (w < a_w - 1)    VisitCell(one_level_cells, a_w, a_h, w+1, h, sum, leaked);
        if (w > 0)          VisitCell(one_level_cells, a_w, a_h, w-1, h, sum, leaked);
        if (h < a_h - 1)    VisitCell(one_level_cells, a_w, a_h, w, h+1, sum, leaked);
        if (h > 0)          VisitCell(one_level_cells, a_w, a_h, w, h-1, sum, leaked);
    }
}
//@param int const * const unsigned short *a_board - argument representing the NxM board.
//@param unsigned short a_w - argument representing the width of the board
//@param unsigned short a_h - argument representing the height of the board
//@return - unsigned __int64 - the volume of water the board retains
// complexity: O(a_w * a_h * max_height)
unsigned __int64 CalculateVolume(const unsigned short *a_board, unsigned short a_w, unsigned short a_h)
{
    if (!a_board || a_w < 3 || a_h < 3)
    {
        return 0;
    }
    // Basic algorithm: slice the board horizontally into as many levels as the maximum height of the board
    // for each sliced level, determine the water volume cubed so far, and the total volume is the sum of the volume of the individual level
    unsigned __int32 n = a_w * a_h;
    unsigned short max_height = 0;
    for (unsigned __int32 i = 0; i < n; ++i)
    {
        if (max_height < a_board[i])
        {
            max_height = a_board[i];
        }
    }
    unsigned short *board = new unsigned short[n];
    memcpy(board, a_board, n * sizeof(unsigned short));
    Cell* one_level_cells = new Cell[n];
    unsigned __int64 total_volume = 0;
    for (unsigned short i = 0; i < max_height; ++i)
    {
        // form a new current level of cells (and update the copy of the board accordingly)
        unsigned __int64 volume_this_level = 0;
        for (unsigned __int32 j = 0; j < n; ++j)
        {
            if (board[j] > 0)
            {
                --board[j];
                one_level_cells[j].height = 1;
            }
            else
            {
                one_level_cells[j].height = 0;
            }
            one_level_cells[j].visited = false;
        }
        // visit all the cells within the current level
        // we mark the cells after being visited, and the cells are visited in "chunks" when they are "connected" together
        // so effectively, most of the top level cell visiting would return immediately, rather than trying to revisit the cells again and again
        for (unsigned short h = 0; h < a_h; ++h)
        {
            for (unsigned short w = 0; w < a_w; ++w)
            {
                unsigned __int64 sum = 0;
                bool leaked = false;
                // NB: the top level function here will attempt to cover *all* the connected cells at the current level (in the recursion)
                // so even though we are still iterating through all the cells at the top level, most of them should find that the cell has been visited
                // so the sum here is actually a "chunked" sum in the perception of the top level cells
                VisitCell(one_level_cells, a_w, a_h, w, h, &sum, &leaked);
                volume_this_level += sum;
            }
        }
        total_volume += volume_this_level;
    }
    delete[] one_level_cells;
    delete[] board;
    return total_volume;
}
int main()
{
    // feel free to play with this board
    unsigned short board[] = {
        2, 2, 2, 2, 2, 2, 2, 2,
        2, 1, 1, 1, 1, 1, 1, 2,
        2, 1, 2, 3, 3, 2, 1, 2,
        2, 1, 3, 1, 1, 3, 1, 2,
        2, 1, 3, 1, 1, 3, 1, 2,
        2, 1, 2, 3, 3, 2, 1, 2,
        2, 1, 1, 1, 1, 1, 1, 2,
        2, 2, 2, 2, 2, 2, 2, 2,
    };
    printf("Volume: %lldn", CalculateVolume(board, 8, 8));
    return 0;
}

这是@padid的伪代码的python(2.7)版本。希望这能帮助到别人。

import heapq
block =[
1, 2, 3,
4 ,2,6,
7, 0,5,
11,15, 13,
14,15,16,
1,0,1,
1,1,1]
cmax =3; 
rmax =7;
items =[]
fence = []
flood = []
waterlevel = 0
total = 0
class Item(object):
    visited = False
    fenced = False
    flooding = False
    height= 0
    index = 0
i=0
## initializing blocks
for val in block:    
    item =  Item();
    item.height = val
    item.index = i
    i+=1
    items.append((item.height,item))
## find out the edges  
for i in range (cmax):
    heapq.heappush(fence, items[i])
    heapq.heappush(fence, items[i+(rmax-1)*cmax])
    print items[i][1].height,items[i+(rmax-1)*cmax][1].height
for i in range(1,rmax-1):
    heapq.heappush(fence, items[cmax*i])
    heapq.heappush(fence, items[cmax*i+(cmax-1)])
    print items[cmax*i][1].height,items[cmax*i+(cmax-1)][1].height
## get neighbour
def get_neighbour(i):
    c= i%cmax
    r= i//cmax
    neighbour = []
    if (c != 0):
        neighbour.append(items[r*cmax+c-1])
    if (c != (cmax -1)):
        neighbour.append(items[r*cmax+c+1])
    if (r != 0):
        neighbour.append(items[(r-1)*cmax+c])
    if (r != (rmax -1)):    
        neighbour.append(items[(r+1)*cmax+c])
    return neighbour

while (len(fence)>0):
    item = heapq.heappop(fence)
    if(item[1].visited):
        continue
    if (item[1].height > waterlevel):
        waterlevel = item[1].height
    heapq.heappush(flood,item)
    item[1].flooding = True
    while(len(flood)>0):
        fitem = heapq.heappop(flood)
        depth = waterlevel - fitem[1].height
        if (depth >= 0):
            total += depth
            fitem[1].visited = True
            neighbour = get_neighbour(fitem[1].index)
            for nitem in neighbour:
                if nitem[1].visited == False :
                    if nitem[1].flooding == False :
                        heapq.heappush(flood,nitem)
                        nitem[1].flooding = True
        else:
            fitem[1].flooding = False
            if fitem[1].fenced == False:
                heapq.heappush(fence,fitem)
                fitem[1].fenced = True
print total