【问题标题】:Biggest forest (Amazon interview question)最大的森林(亚马逊面试题)
【发布时间】:2020-09-15 07:48:51
【问题描述】:

每个单元格要么是水'W',要么是一棵树'T'。给定有关该字段的信息,打印最大森林的大小。森林的大小是其中树木的数量。为了清楚起见,请参阅示例案例

输入:

第一行包含矩阵 N 的大小。 接下来的 N 行每行包含 N 个字符,“W”或“T”。

输出:

打印最大森林的大小。

示例输入:

5
TTTWW
TWWTT
TWWTT
TWTTT
WWTTT

预期输出:10

我的代码:

t_cases = int(input())
k1 = 0
k2 = 0
for _ in range(t_cases):
    list1 = (input())
    z = 0
    list2 = []
    for i in range(len(list1)):
        z = list1.count('T')
        if list1[i] == "W":
            break
        elif list1[i] == "T":
            list2.append(list1[i])
            
    k1 = k1 + list2.count('T')
    if z > list2.count('T'):
        k2 = k2 + (z - list2.count('T'))
    else: 
        k2 = k2 + (list2.count('T')- z)
if k1 > k2:
    print(k1)
else: 
    print(k2)

我的代码满足示例输入,但每个测试用例都失败了。此代码在所有情况下计算'W'之前的tress总和并将它们添加到k1。同样,k2 是 'W' 之后的所有树的总和。

注意:也可以使用递归!

【问题讨论】:

  • 该代码适用于正是这种模式类型的字段(两片森林被一条从上到下的“河流”隔开),但几乎适用于任何其他模式。
  • 隐藏的测试用例可能有像 WTTWTT 这样的输入?那可能是我的代码失败了?
  • 嗯,是的。几乎所有输入的代码都会失败。
  • 这让我想起了“扫雷”游戏。
  • 这就是问题所在,代码是专门解决示例输入的,不是一般情况。

标签: python algorithm recursion flood-fill


【解决方案1】:

这本质上是变相的经典flood-fill algorithm。对于您看到的每棵树,您可以运行洪水填充以查找同一森林中的所有树,然后您只需要返回您找到的最大树数。

一种进行洪水填充的方法是使用breadth-first search。这是一些简单的伪代码;因为这是一个面试练习题,所以我将把翻译留作练习。

max_forest = 0
for each location:
    if it's a tree, and you haven't visited it yet:
        max_forest = max(max_forest, size_of_forest(location))
return max_forest

size_of_forest(location):
    if this location has been visited already, return 0
    make a worklist of locations, initially just the start.
    
    size = 1
    while the worklist isn't empty:
        remove one element from the worklist.
        increment size.

        for each neighboring tree:
            if that location isn't yet visited:
                mark that location visited.
                increment size.
                add the location to the worklist.

        return size

另一种方法是使用depth-first search。这是一些伪代码:

size_of_forest(location):
    if this location is visited, return 0
    mark this location as visited

    result = 0
    for each neighboring tree:
        result += size_of_forest(that tree)

    return result

您需要解决很多问题才能将其转化为工作代码。您将如何跟踪访问过哪些位置?你将如何迭代相邻的树?

更抽象地说,这个问题相当于找到由每棵树有一个节点形成的图的最大connected component 的大小,当它们彼此相邻时,树之间有边。我在这里给出的 BFS 和 DFS 伪代码是通用的 BFS 和 DFS 算法,专门针对这种特殊情况。

这两种算法 - BFS 和 DFS - 非常适合了解您是否正在进行工作面试。它们在实践中一直出现,一旦你知道如何使用它们,它们就是真正的主力军。 (我已经数不清自己需要编写多少次代码了!)

【讨论】:

  • 代码是什么语言的?它似乎是某种形式的伪代码?老实说,编写可运行/可验证的 Python 代码需要更多的努力和几行代码(Python 本质上是我所知道的最接近伪代码的语言。)
  • @ldog 这是伪代码。这是故意的 - 由于 OP 正在处理面试的练习问题,我认为让他们完成将 BFS 和 DFS 转换为针对此特定问题的代码的细节会很好。
【解决方案2】:

这很容易实现,例如python 使用简单的呼吸优先洪水填充算法。重点是,您需要回溯以确保正确搜索森林。试试这样的

ex0="""
5
TTTWW
TWWTT
TWWTT
TWTTT
WWTTT
"""


def parse(data):
    """Returns the fields as a set of coordniates"""
    lines = iter(data.splitlines())
    next(lines)  # skip the size
    field = set()
    for y, line in enumerate(lines):
        for x, cell in enumerate(line):
            if cell == 'T':
                field.add((x, y))
    return field


DIRECTIONS = [(-1, 0), (1, 0), (0, -1), (0, 1)]


def neighbors(p):
    x, y = p
    for dx, dy in DIRECTIONS:
        yield x + dx, y + dy


def find_forests(field):
    unvisited = set(field)  # copy the field
    while unvisited:
        first = unvisited.pop()  # take any tree
        queue = [first]
        forest = set()
        while queue:
            p = queue.pop(0)
            if p in unvisited:
                unvisited.remove(p)
            forest.add(p)
            for n in neighbors(p):
                if n in unvisited:
                    queue.append(n)
        yield forest

forests = find_forests(parse(ex0))
print(max(map(len, forests)))  # find largest forest

【讨论】:

    【解决方案3】:

    解决方案:

    from collections import deque
    
    n = int(input())
    a = []
    for i in range(n):
        a.append(input())
    
    used = [[False for i in range(n)] for j in range(n)]
    ans = 0
    for i in range(n):
        for j in range(n):
            if used[i][j] or a[i][j] == 'W':
                continue
            q = deque()
            q.append((i, j))
            used[i][j] = True
            cnt = 0
            while q:
                (x, y) = q.pop()
                cnt += 1
                for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                    x_ = x + dx
                    y_ = y + dy
                    if x_ >= 0 and x_ < n and y_ >= 0 and y_ < n:
                        if not used[x_][y_] and a[x_][y_] == 'T':
                            q.append((x_, y_))
                            used[x_][y_] = True
            ans = max(ans, cnt)
    print(ans)
    

    解释:

    used - NxN 数组,其中 True 表示该单元格已被访问。 我们遍历所有细胞。如果我们找到T,并且在我们开始计算森林之前没有访问它(cnt)。为此,我们检查右侧、左侧、顶部和底部单元格。如果它们包含未访问的T,那么我们将其添加到我们的森林中。就是BFS算法。

    【讨论】:

      【解决方案4】:

      考虑“探索”森林的迭代或递归方法 - 从一棵树开始并找到所有相邻的树,然后找到与这些相邻的树。然后找到一种方法来标记已经访问过的树,并在遍历矩阵时跳过它们。

      【讨论】:

        【解决方案5】:

        由于没有人提及,这里尝试了一种标签方案,该方案在矩阵的每一行上按顺序迭代,并使用两行和一个标签字典作为额外空间:

        def f(m):
          n = len(m)
          
          # Stores final label and size
          # for each component
          labels = {}
          row1 = [0] * n
          row2 = [0] * n
          
          label = 0
                
          for i in range(n):
            for j in range(n):
              if m[i][j] == "T":
                if j == 0 or not row2[j-1]:
                  # No label above or to the left
                  if i == 0 or not row1[j]:
                    label += 1
                    labels[label] = {"size": 1, "label": label}
                    row2[j] = label
                    print("i, j: %s, %s; new label %s" % (i, j, label))
                  # Label only above
                  else:
                    row2[j] = row1[j]
                    labels[row1[j]]["size"] += 1
                    print("i, j: %s, %s; continuing above label %s" % (i, j, labels[row1[j]]["label"]))
                # Label only to the left
                elif not row1[j]:
                  row2[j] = row2[j-1]
                  labels[row2[j-1]]["size"] += 1
                  print("i, j: %s, %s; continuing left label %s" % (i, j, labels[row2[j-1]]["label"]))
                # Labels above and to the left
                else:
                  row2[j] = row1[j]
                  labels[row1[j]]["size"] += 1
                  print("i, j: %s, %s; continuing above label %s" % (i, j, labels[row1[j]]["label"]))
                  # Unequal labels above and to the left,
                  # relabel
                  if labels[row1[j]]["label"] != labels[row2[j-1]]["label"]:
                    print("i, j: %s, %s; relabeling %s to %s" % (i, j, labels[row2[j-1]]["label"], labels[row1[j]]["label"]))
                    labels[row2[j-1]]["label"] = labels[row1[j]]["label"]
            row1 = row2
            row2 = [0] * n
            to_del = set()
            for idx, lbl in enumerate(row1):
              if lbl and labels[lbl]["label"] != lbl:
                to_del.add(lbl)
                row1[idx] = labels[lbl]["label"]
            for lbl in to_del:
              target = labels[lbl]["label"]
              print("i: %s; combining label %s with label %s" % (i, lbl, target))
              labels[target]["size"] += labels[lbl]["size"]
              del labels[lbl]
                
        
          return labels
                  
                  
        import sys
        
        data = sys.stdin.readlines()
        print(f(data))
        

        标准输入:

        TTTWW
        TWWTT
        TWWTT
        TWTTT
        WWTTT
        

        标准输出:

        i, j: 0, 0; new label 1
        i, j: 0, 1; continuing left label 1
        i, j: 0, 2; continuing left label 1
        i, j: 1, 0; continuing above label 1
        i, j: 1, 3; new label 2
        i, j: 1, 4; continuing left label 2
        i, j: 2, 0; continuing above label 1
        i, j: 2, 3; continuing above label 2
        i, j: 2, 4; continuing above label 2
        i, j: 3, 0; continuing above label 1
        i, j: 3, 2; new label 3
        i, j: 3, 3; continuing above label 2
        i, j: 3, 3; relabeling 3 to 2
        i, j: 3, 4; continuing above label 2
        i: 3; combining label 3 with label 2
        i, j: 4, 2; continuing above label 2
        i, j: 4, 3; continuing above label 2
        i, j: 4, 4; continuing above label 2
        {1: {'size': 6, 'label': 1}, 2: {'size': 10, 'label': 2}}
        

        标准输入:

        WTTTT
        WWWWT
        WTTTT
        WWTWT
        WTTWW
        

        标准输出:

        i, j: 0, 1; new label 1
        i, j: 0, 2; continuing left label 1
        i, j: 0, 3; continuing left label 1
        i, j: 0, 4; continuing left label 1
        i, j: 1, 4; continuing above label 1
        i, j: 2, 1; new label 2
        i, j: 2, 2; continuing left label 2
        i, j: 2, 3; continuing left label 2
        i, j: 2, 4; continuing above label 1
        i, j: 2, 4; relabeling 2 to 1
        i: 2; combining label 2 with label 1
        i, j: 3, 2; continuing above label 1
        i, j: 3, 4; continuing above label 1
        i, j: 4, 1; new label 3
        i, j: 4, 2; continuing above label 1
        i, j: 4, 2; relabeling 3 to 1
        i: 4; combining label 3 with label 1
        {1: {'size': 13, 'label': 1}}
        

        【讨论】:

          猜你喜欢
          • 2019-11-06
          • 2011-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-02
          • 1970-01-01
          • 2021-03-02
          相关资源
          最近更新 更多