关于在3d棋盘中找到水量的提示

时间:2013-02-22 21:32:45

标签: c++ recursion volume

所以我有一个任务,我必须重新创建一个3d棋盘,这是一个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;
    }

所有我发现的是顶部,底部,左侧和右侧的正方形高度。

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

非常感谢任何帮助。没有找到答案只是为了向正确的方向推进我需要做的事情。

5 个答案:

答案 0 :(得分:3)

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

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

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

现在你这样循环:

弹出fence中的前项。您已经选择了周边的最低点之一。

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

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

Flooding就是这样:循环直到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

答案 1 :(得分:0)

这不仅仅是计算音量。

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

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

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

答案 2 :(得分:0)

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

您可能会尝试在概念上将电路板分成多个层,例如:

-------------------------
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 :(得分:0)

这是一段工作代码:基本思想是将电路板水平切割成各级,并确定每个级别可以容纳的音量(复杂度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: %lld\n", CalculateVolume(board, 8, 8));
    return 0;
}

答案 4 :(得分:0)

这是@paddy的伪代码的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