【问题标题】:Finding all nodes on a graph that are N nodes away查找图上距离 N 个节点的所有节点
【发布时间】:2020-09-25 23:26:51
【问题描述】:

你和几个朋友正在玩棋盘游戏。游戏的棋盘布局在一个具有许多循环的大型互连图中。每个玩家从棋盘上的不同位置开始。轮到你时,你可以掷出 1 到 6 个 6 面骰子之间的任何东西(换句话说,1 到 36 之间的任何东西)。您如何确定从当前位置转一圈可以到达的每个空间? (例如:我掷出 13。找出棋盘上距离我 13 格的所有点。) 你只能向前移动,但你可以绕圈走一圈,总的数小于你的掷骰数.

示例:如果这是您的图表,并且您从左上角开始并掷出 6,那么您可以移动的位置是下、右、右、上、左、左。 但是你不能向右、向左、向右、向左、向右、向左移动

  o---o---o---o---o
  |       |   |
  o---o---o---o

有没有比深度优先搜索更好的算法?

【问题讨论】:

  • 是作业的一部分还是你的问题?此外,路径中是否存在循环,例如你能在两个节点之间来回走 6 次,然后再往前走吗?
  • @tobias_k 这是我的问题。
  • 您应该使用广度优先搜索。这是最佳选择。
  • @ciamej 为什么选择广度优先?
  • 深度优先搜索(递归深度受附加参数限制)可用于解决所描述的问题(您显然很清楚)。可达后继区域内的每条边最多将被遍历一次。如果输入图是一棵树,它将被准确地遍历一次。我认为没有比这更好的可能性了。否则,算法的结果不可能依赖于整个输入。亚线性运行时间是一种罕见的现象。

标签: algorithm graph-algorithm graph-traversal


【解决方案1】:

您根本不需要真正遍历图表来解决这个问题。

该图可以通过其邻接矩阵进行编码:矩阵M,如果从节点i到节点jM(i,j) = 0之间存在一条边,则M(i,j) = 1如果没有优势。

这个矩阵有一个超酷的属性:对于任何非负整数kM**kk-th power of MM乘以它自己k 次)是这样的,(M**k)(i,j) = 从ij 的不同步行次数。

如果(M**k)(i,j) > 0,则节点i 可以从节点j 到达恰好k 移动。请注意,如果您确保每个节点都有自己的边,,如果M 的对角线充满了1s,那么恰好在@ 中可以到达的节点987654342@moves 与最多kmoves 中可以到达的节点相同。

另请参阅:https://en.wikipedia.org/wiki/Adjacency_matrix#Matrix_powers

在大多数编程语言中,都有一些库可以非常有效地处理矩阵和矩阵运算,因此将矩阵乘以幂可以比实际访问一个接一个地访问图的节点要快得多。

另一方面,如果图很大而k 很小,并且您只对一个起始节点感兴趣,那么计算M**k 的效率可能低于图遍历,因为M**k 回答了这个问题对于图表的每个节点,而不仅仅是您感兴趣的起始节点。

但如果您对所有可能的起始节点感兴趣,或者如果k 接近图的直径,那么计算M**k 很可能是最佳选择。

【讨论】:

  • 虽然正确,但我不确定这是否比深度优先搜索更好。深度优先搜索的运行时间为O(|E|),无论所需距离如何k,而矩阵求幂的运行时间为O(|E|^{2k}),其中|E| is the number of edges。此外,矩阵求幂不会受益于图的稀疏性。
  • 这很有趣,但是 (a) 这实际上是否比实际执行 DFS 或 BFS 更快,并且 (b) 这是否可以找到所有完全正确的节点13 转身考虑循环?
  • 是的,它专门为您提供长度正好为 13 的步数。如果您想知道在 13 次移动中可以到达哪些节点,无法到达12 步或更少,那么您应该同时计算 M**12M**13(请注意,从 M**12 计算 M**13 速度很快,因为 M**13 = M * M**12)。
  • 是否考虑到不能使用与上一步相同的边往回走的限制?
  • “正好13”和“最多13”的区别是通过判断一个节点到自身是否有边来完成的;如果有,那么您可以“通过”您的 13 个动作之一;如果没有,那么您必须移动 13 步。
【解决方案2】:

限制你可以绕圈子,但不能直接回到你来自的节点,这实际上是一个非常有趣的问题。特别是,您既不能只执行 BFS 或 DFS 并在较少的移动中修剪您已经访问过的所有节点,也不能使用巧妙的矩阵乘法。

相反,您可以使用 DFS 的变体,但您必须跟踪可以到达某个节点的移动次数,以及您在访问该节点时来自哪个节点,如果您只修剪分支之前已经看到 exact 组合 - 如果您在 较少 移动中到达节点,或者来自另一个节点,则不会。

Python中的基本实现及示例:

def search(graph, start, moves):
    stack = [(start, 0, -1)]
    distance = {i: set() for i in range(moves+1)}
    while stack:
        node, dist, prev = stack.pop()
        if (node, prev) in distance[dist]: continue
        distance[dist].add((node, prev))
        if dist < moves:
            stack.extend((x, dist+1, node) for x in graph[node] if x != prev)
    return {node for (node, prev) in distance[moves]}

# 1---2---3---4---5
# |       |   |
# 6---7---8---9
g = {1: [2,6], 2: [1,3], 3: [2,4,8], 4: [3,5,9], 5: [4],
     6: [1,7], 7: [6,8], 8: [3,7,9], 9: [4,8]}
print(search(g, 1, 13))
# {8, 2, 4, 6}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2015-11-19
    相关资源
    最近更新 更多