简体   繁体   English

二维阵列的水容量

[英]Water capacity of a 2D array

I have to do a little exercise at my university but I am already stuck for a while. 我必须在我的大学做一些运动,但我已经停留了一段时间。 The exercise is about calculating the water capacity of a 2D array, the user has to enter the width (w) and the height (h) of the 2D array, and then all the elements of the array, which represent the height at that location. 练习是关于计算2D阵列的水容量,用户必须输入2D阵列的宽度(w)和高度(h),然后输入阵列的所有元素,它们代表该位置的高度。 Really simple example: 非常简单的例子:

10 10 10
10 2 10
10 10 10

The output will then be 8, because that is the maximum water that fits in there. 输出将是8,因为那是适合那里的最大水。 Another example is: 另一个例子是:

 6 4
 1 5 1 5 4 3
 5 1 5 1 2 4
 1 5 1 4 1 5
 3 1 3 6 4 1

Output will be 14. 输出将是14。

What also important to mention is: The width and height of the array can not be larger than 1000 and the heights of the element cannot be larger than 10^5. 另外值得一提的是:阵列的宽度和高度不能大于1000,并且元素的高度不能大于10 ^ 5。

Now I basically have the solution, but it is not fast enough for larger inputs. 现在我基本上有了解决方案,但对于更大的输入来说它还不够快。 What I did is the following: I add the heights to a TreeSet and then every time I poll the last one (the highest) and then I go through the array (not looking at the edges) and use DFS and check for every position if the water can stay in there. 我做的是以下内容:我将高度添加到TreeSet,然后每次轮询最后一个(最高),然后我遍历数组(不查看边缘)并使用DFS并检查每个位置是否水可以留在那里。 If the water doesn't go out of the array than calculate the positions that are under water, if it goes out of the array then poll again and do the same. 如果水没有离开阵列而不是计算水下的位置,如果它离开阵列,那么再次轮询并做同样的事情。

I also tried looking at the peaks in the array, by going vertically and horizontally. 我也尝试通过垂直和水平方向观察阵列中的峰值。 For the example above you get this: 对于上面的例子,你得到这个:

0 5 0 5 4 0
5 0 5 0 0 4
0 5 0 4 0 5
3 1 3 6 4 0

What I did with this was give the peaks a color let say (black) and then for all the white colors take the minimum peak value with DFS again and then take that minimum to calculate the water capacity. 我对此做的是给峰值一个颜色说(黑色)然后对于所有白色颜色再次采用DFS的最小峰值,然后采取最小值来计算水容量。 But this doesn't work, because for example: 但这不起作用,因为例如:

7 7 7 7 7
7 4 4 4 7
7 2 3 1 7
7 4 4 4 7
7 7 7 7 7

Now 3 is a peak, but the water level is 7 everywhere. 现在3是一个高峰,但到处都是水位7。 So this won't work. 所以这不会奏效。

But because my solution is not fast enough, I am looking for a more efficient one. 但由于我的解决方案不够快,我正在寻找一种更有效的解决方案。 This is the part of the code where the magic happens: 这是魔术发生的代码的一部分:

    while (p.size() != 0 || numberOfNodesVisited!= (w-2)*(h-2)) {
        max = p.pollLast();
        for (int i=1; i < h-1; i++) {
            for (int j=1; j < w-1; j++) {
                if (color[i][j] == 0) {
                    DFSVisit(profile, i, j);
                    if (!waterIsOut) {
                        sum+= solveSubProblem(heights, max);
                        numberOfNodesVisited += heights.size();
                        for(int x = 0; x < color.length; x++) {
                            color2[x] = color[x].clone();
                        }
                    } else {
                        for(int x = 0; x < color2.length; x++) {
                            color[x] = color2[x].clone();
                        }
                        waterIsOut = false;
                    }
                    heights.clear();
                }
            }
        }
   }

Note I am resetting the paths and the colors every time, I think this is the part that has to be improved. 注意我每次都重置路径和颜色,我认为这是必须改进的部分。

And my DFS: I have three colors 2 (black) it is visited, 1 (gray) if it is an edge and 0 (white) if is not visited and not an edge. 而我的DFS:我有三种颜色2(黑色)它被访问,1(灰色)如果是边缘而0(白色)如果没有被访问而不是边缘。

 public void DFSVisit(int[][] profile, int i, int j) {
    color[i][j] = 2; // black
    heights.add(profile[i][j]);
    if (!waterIsOut && heights.size() < 500) { 
        if (color[i+1][j] == 0 && max > profile[i+1][j]) { // up
            DFSVisit(profile, i+1, j);
        } else if (color[i+1][j] == 1 && max > profile[i+1][j]) {
            waterIsOut = true;
        }
        if (color[i-1][j] == 0 && max > profile[i-1][j]) { // down
            DFSVisit(profile, i-1, j);
        } else if (color[i-1][j] == 1 && max > profile[i-1][j]) {
            waterIsOut = true;
        }
        if (color[i][j+1] == 0 && max > profile[i][j+1]) { // right
            DFSVisit(profile, i, j+1);
        } else if (color[i][j+1] == 1  && max > profile[i][j+1]) {
            waterIsOut = true;
        }
        if (color[i][j-1] == 0  && max > profile[i][j-1]) { //left
            DFSVisit(profile, i, j-1);
        } else if (color[i][j-1] == 1  && max > profile[i][j-1]) {
            waterIsOut = true;
        }
    }
}

UPDATE @dufresnb referred to talentbuddy.co where the same exercise is given at https://www.talentbuddy.co/challenge/526efd7f4af0110af3836603 . 更新 @dufresnb提到talentbuddy.co,其中相同的练习在https://www.talentbuddy.co/challenge/526efd7f4af0110af3836603上给出。 However I tested al lot of solutions and a few of them actually make it through my first four test cases, most of them however already fail on the easy ones. 然而,我测试了很多解决方案,其中一些实际上通过我的前四个测试用例,但大多数已经在简单的测试用例中失败了。 Talent buddy did a bad job on making test cases: in fact they only have two. 人才伙伴在制作测试用例方面做得不好:实际上他们只有两个。 If you want to see the solutions they have just register and enter this code (language C): it is enough to pass their test cases 如果您想查看他们刚刚注册的解决方案并输入此代码(语言C):它足以通过他们的测试用例

#include <stdio.h>

void rain(int m, int *heights, int heights_length) {
    //What tests do we have here?
    if (m==6)
        printf("5");
    else if (m==3)
        printf("4");
    //Looks like we need some more tests.
}

UPDATE @tobias_k solution is a working solution, however just like my solution it is not efficient enough to pass the larger input test cases, does anyone have an idea for an more efficient implementation? 更新 @tobias_k解决方案是一个可行的解决方案,但是就像我的解决方案一样,它没有足够的效率来传递更大的输入测试用例,是否有人有更高效实现的想法?

Any ideas and help will be much appreciated. 任何想法和帮助将不胜感激。

Here's my take on the problem. 这是我对这个问题的看法。 The idea is as follows: You repeatedly flood-fill the array using increasing "sea levels". 这个想法如下:你使用增加的“海平面”反复填充阵列。 The level a node is first flooded will be the same level that the water would stay pooled over that node when the "flood" retreats. 节点首次被淹没的级别将与“洪水”撤退时水将保持在该节点上的水平相同。

  • for each height starting from the lowest to the highest level: 从最低到最高的每个高度:
    • put the outer nodes into a set, called fringe 将外部节点放入一个名为fringe的集合中
    • while there are more nodes in the fringe set, pop a node from the set 当边缘集中有更多节点时,从集合中弹出一个节点
      • if this node was first reached in this iteration and its height is lesser or equal to the current flood height, memorize the current flood height for tha tnode 如果在此迭代中首次到达此节点并且其高度小于或等于当前洪水高度,则记住当前洪水高度以获取该节点
      • add all its neighbours that have not yet been flooded and have a height lesser or equal to the current flood height to the fringe 添加所有尚未被淹没的邻居,其高度小于或等于当前洪水高度到边缘

As it stands, this will have compexity O(nmz) for an nxm array with maximum elevation z , but with some optimization we can get it down to O(nm) . 就目前而言,对于具有最大高程znxm阵列,这将具有复合O( nmz ,但是通过一些优化,我们可以将其降低到O(nm) For this, instead of using just one fringe, and each time working our way from the outside all the way inwards, we use multiple fringe sets, one for each elevation level, and put the nodes that we reach in the fringe corresponding to their own height (or the current fringe, if they are lower). 为此,我们不是只使用一个边缘,而是每次从外面一直向内工作,我们使用多个边缘集合,每个高程级别一个,并将我们到达的节点放在对应于他们自己的边缘高度(或当前条纹,如果它们更低)。 This way, each node in the array is added to and removed from a fringe exactly once . 这样,阵列中的每个节点都被添加到条纹中并从条带中移除一次 And that's as fast as it possibly gets. 这和它可能的速度一样快。

Here's some code. 这是一些代码。 I've done it in Python, but you should be able to transfer this to Java -- just pretend it's executable pseudo-code. 我已经在Python中完成了它,但你应该能够将它传递给Java - 只是假装它是可执行的伪代码。 You can add a counter to see that the body of the while loop is indeed executed 24 times, and the result, for this example, is 14. 您可以添加一个计数器,以查看while循环的主体确实执行了24次,并且此示例的结果为14。

# setup and preparations
a = """1 5 1 5 4 3
       5 1 5 1 2 4
       1 5 1 4 1 5
       3 1 3 6 4 1"""
array = [[int(x) for x in line.strip().split()] 
         for line in a.strip().splitlines()]
cols, rows = len(array[0]), len(array)
border = set([(i, 0     ) for i in range(rows)] + 
             [(i, cols-1) for i in range(rows)] + 
             [(0, i     ) for i in range(cols)] + 
             [(rows-1, i) for i in range(cols)])
lowest  = min(array[x][y] for (x, y) in border) # lowest on border
highest = max(map(max, array))                  # highest overall

# distribute fringe nodes to separate fringes, one for each height level
import collections
fringes = collections.defaultdict(set) # maps points to sets
for (x, y) in border:
    fringes[array[x][y]].add((x, y))

# 2d-array how high the water can stand above each cell
fill_height = [[None for _ in range(cols)] for _ in range(rows)]
# for each consecutive height, flood-fill from current fringe inwards
for height in range(lowest, highest + 1):
    while fringes[height]: # while this set is non-empty...
        # remove next cell from current fringe and set fill-height
        (x, y) = fringes[height].pop()
        fill_height[x][y] = height
        # put not-yet-flooded neighbors into fringe for their elevation
        for x2, y2 in [(x-1, y), (x, y-1), (x+1, y), (x, y+1)]:
            if 0 <= x2 < rows and 0 <= y2 < cols and fill_height[x2][y2] is None:
                # get fringe for that height, auto-initialize with new set if not present
                fringes[max(height, array[x2][y2])].add((x2, y2))

# sum of water level minus ground level for all the cells
volume = sum(fill_height[x][y] - array[x][y] for x in range(cols) for y in range(rows))
print "VOLUME", volume

To read your larger test cases from files, replace the a = """...""" at the top with this: 要从文件中读取较大的测试用例,请使用以下命令替换顶部的a = """..."""

with open("test") as f:
    a = f.read()

The file should contain just the raw array as in your question, without dimension information, separated with spaces and line breaks. 该文件应该只包含问题中的原始数组,没有维度信息,用空格和换行符分隔。

talentbuddy.co has this problem as one of their coding tasks. talentbuddy.co将此问题作为其编码任务之一。 It's called rain, if you make an account you can view other peoples solutions. 它被称为下雨,如果你创建一个帐户,你可以查看其他人的解决方案。

#include <iostream>
#include <vector>

bool check(int* myHeights, int x, int m, bool* checked,int size)
{
    checked[x]=true;
    if(myHeights[x-1]==myHeights[x] && (x-1)%m!=0 && !checked[x-1])
    {
        if(!check(myHeights,x-1,m,checked,size))return false;
    }
    else if((x-1)%m==0 && myHeights[x-1]<=myHeights[x])
    {
        return false;
    }
    if(myHeights[x+1]==myHeights[x] && (x+1)%m!=m-1 && !checked[x+1])
    {
        if(!check(myHeights,x+1,m,checked,size))return false;
    }
    else if((x+1)%m==m-1 && myHeights[x+1]<=myHeights[x])
    {
        return false;
    }
    if(myHeights[x-m]==myHeights[x] && (x-m)>m && !checked[x-m])
    {
        if(!check(myHeights,x-m,m,checked,size))return false;
    }
    else if((x-m)<m && myHeights[x-m]<=myHeights[x])
    {
        return false;
    }
    if(myHeights[x+m]==myHeights[x] && (x+m)<size-m && !checked[x+m])
    {
        if(!check(myHeights,x+m,m,checked,size))return false;
    }
    else if((x+m)>size-m && myHeights[x+m]<=myHeights[x])
    {
        return false;
    }
    return true;
}

void rain(int m, const std::vector<int> &heights) 
{
    int total=0;
    int max=1;
    if(m<=2 || heights.size()/m<=2)
    {
        std::cout << total << std::endl;
        return;
    }
    else
    {
        int myHeights[heights.size()];
        for(int x=0;x<heights.size();++x)
        {
            myHeights[x]=heights[x];
        }
        bool done=false;
        while(!done)
        {
            done=true;
            for(int x=m+1;x<heights.size()-m;++x)
            {
                if(x<=m || x%m==0 || x%m==m-1)
                {
                    continue;
                }

                int lower=0;
                if(myHeights[x]<myHeights[x-1])++lower;
                if(myHeights[x]<myHeights[x+1])++lower;
                if(myHeights[x]<myHeights[x-m])++lower;
                if(myHeights[x]<myHeights[x+m])++lower;

                if(lower==4)
                {
                    ++total;
                    ++myHeights[x];
                    done=false;
                }
                else if(lower>=2)
                {
                    bool checked[heights.size()];
                    for(int y=0;y<heights.size();++y)
                    {
                        checked[y]=false;
                    }
                    if(check(myHeights,x,m,checked,heights.size()))
                    {
                        ++total;
                        ++myHeights[x];
                        done=false;
                    }
                }
            }
        }
    }
    std::cout << total << std::endl;
    return;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM