微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 BFS 的蛇梯

如何解决使用 BFS 的蛇梯

给定一个蛇梯板,我们必须找到最后一个顶点到第 0 个顶点的最小距离。 (在第 0 个顶点,我们掷骰子并继续前进)

在此处阅读有关问题的信息 -> LINK

我的代码

from collections import defaultdict

global INT_MAX
INT_MAX = 3 ** 38


class Graph:
    def __init__(self):
        self.vertList = defaultdict(list)

    def addEdge(self,u,v):
        self.vertList[u].append(v)

    def distanceBFS(self,src,target):
        queue = []
        queue.append(src)
        distanceDict = {}
        for v in self.vertList:
            distanceDict[v] = INT_MAX
        distanceDict[src] = 0
        visited = set()

        while (len(queue) > 0):
            curr = queue.pop(0)
            visited.add(curr)

            for vertex in self.vertList[curr]:
                if vertex not in visited:
                    queue.append(vertex)
                    distanceDict[vertex] = distanceDict[curr] + 1

        print(distanceDict[target])

    def solveSnakesLadder(self):
        snakeLadderDict = {2: 13,5: 2,9: 18,18: 11,17: -13,20: -14,24: -8,25: 10,32: -2,34: -22}
        # we have 36 Boxes --> i
        # we can throw a dice at each of these 36 Boxes and it can be from 1 to 6
        # j is new position on the board after throwing a dice
        for i in range(0,37):
            for dice in range(1,7):
                j = i + dice
                if j in snakeLadderDict:
                    j += snakeLadderDict[j]
                if j <= 36:
                    self.addEdge(i,j)
        self.distanceBFS(0,36)


g = Graph()
g.solveSnakesLadder()

当前输出

10

正确的输出

4

在这里做错了什么?跟逻辑有关系吗?我也可以在添加到队列之前检查 distanceDict 中的值,但访问集做同样的事情!

解决方法

基本上,问题在于以下代码块:

    queue.append(src)
    distanceDict[src] = 0
    visited = set()
    while (len(queue) > 0):
        curr = queue.pop(0)
        visited.add(curr)

        for vertex in self.vertList[curr]:
            if vertex not in visited:
                queue.append(vertex)
                distanceDict[vertex] = distanceDict[curr] + 1

我将尝试用以下基本图表来解释相同的内容:

enter image description here

假设,您需要在下图中找到从顶点 1 到顶点 3 的最短路径。使用上述实现,您将进行以下迭代:

  1. 将顶点 1 添加到队列 Q 并将其距离设置为 0
  2. 接下来,您在 1 循环中从 Q 中弹出 while 并将其标记为已访问
  3. 接下来,您访问它的所有邻居并将它们添加到队列 Q

所以,此时不同对象的状态会如下:

我们在队列 2 中有顶点 3Qdistance 字典将作为 distance[1] = 0,distance[2] = 1,distance[3] = 1,这是问题开始的地方,因为我们尚未将顶点 23 添加到 visited 数组,如果我们从其他邻居再次访问它们,我们将在下一次迭代中再次开始更新这些顶点的距离。

  1. 再次回到我们的算法,现在我们再次转到 while 循环的起点和 pop 顶点 2 并将其标记为已访问。
  2. 当我们下一次遍历它未访问的邻居时,我们将再次访问我们已经访问过的 3,并将使用不正确的值更新 distance 字典。

不同对象访问顶点2及其邻居后的状态如下:

队列中有顶点 3,距离字典为 distance[1] = 0,distance[3] = 2。正如我们从迭代中看到的那样,distance[3] 被错误地更新。

然而,修复非常简单,我们需要在访问顶点后将其标记为已访问,这样我们就不会通过其他邻居再次将其放入队列中。

到现在为止,您可能已经猜到 BFS 函数的正确实现应该是给定的:

from collections import defaultdict

global INT_MAX
INT_MAX = 3 ** 38


class Graph:
    def __init__(self):
        self.vertList = defaultdict(list)

    def addEdge(self,u,v):
        self.vertList[u].append(v)

    def distanceBFS(self,src,target):
        queue = []
        queue.append(src)
        distanceDict = {}
        for v in self.vertList:
            distanceDict[v] = INT_MAX
        distanceDict[src] = 0
        visited = set()
        visited.add(src)
        while (len(queue) > 0):
            curr = queue.pop(0)

            for vertex in self.vertList[curr]:
                if vertex not in visited:
                    queue.append(vertex)
                    visited.add(vertex)
                    distanceDict[vertex] = distanceDict[curr] + 1

        print(distanceDict[target])



    def solveSnakesLadder(self):
        snakeLadderDict = {2: 13,5: 2,9: 18,18: 11,17: -13,20: -14,24: -8,25: 10,32: -2,34: -22}
        # we have 36 boxes --> i
        # we can throw a dice at each of these 36 boxes and it can be from 1 to 6
        # j is new position on the board after throwing a dice
        for i in range(0,37):
            for dice in range(1,7):
                j = i + dice
                if j in snakeLadderDict:
                    j += snakeLadderDict[j]
                if j <= 36:
                    self.addEdge(i,j)
        self.distanceBFS(0,36)


g = Graph()
g.solveSnakesLadder()
,

您的算法中有两个错误。最重要的在这里:

distanceDict[vertex] = distanceDict[curr] + 1

这可能会用更糟糕的距离覆盖现有距离。所以你应该这样做:

distanceDict[vertex] = min(distanceDict[vertex],distanceDict[curr] + 1)

而且,distanceDict 中缺少一个顶点,因为节点 36 没有出边。这可能不是真正的错误,而是遗漏。但是在您的图表中为所有 节点设置一个条目是合乎逻辑的。所以你应该处理它,例如通过初始化:

distanceDict[target] = INT_MAX

找到目标后退出循环也是一个好主意,以避免不必要地访问所有其他节点,这永远不会改变结果。

最后,不需要跟踪每个节点到源的距离,也不需要使用队列。如果将 BFS 遍历树的每个“级别”分开,那么您可以为每个级别使用一个新列表,并增加一个距离变量来跟踪它。

def distanceBFS(self,target):
    distance = 0
    visited = set()
    frontier = [src]
    while frontier:
        if target in frontier:
            return distance  # found
        visited.update(frontier)
        frontier = [vertex for curr in frontier for vertex in self.vertList[curr] 
                               if vertex not in visited]
        distance += 1
    return -1  # cannot be reached

注意这个函数返回距离,所以你仍然需要打印它:

print(self.distanceBFS(0,36))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。