我最近在今年早些时候 Spotify 的黑客挑战中遇到了这个(编辑:问题 A)有趣的问题,其中涉及确定火车卡车交叉点的切换以将火车路由回其起点。火车必须朝着它离开的方向到达,并且火车永远不能在轨道上倒车。
据我了解,该问题可以建模为无向(?)图,我们必须从某个顶点找到最短的循环,或者检测不存在这样的循环。然而,有趣的是,对于顶点 v,与 v 相邻的顶点依赖于到 v 的路径,因此从某种意义上说,该图可以被认为是有向的,尽管这个方向是依赖于路径的。
我的第一个想法是将每个节点建模为 3 个单独的顶点 A、B 和 C,其中 A <-> B 和 A <-> C,然后使用广度优先搜索构建搜索树,直到找到原始顶点,但这因上面的警告而变得复杂,即给定顶点的邻接取决于我们访问的前一个顶点。这意味着在我们的 BFS 树中,节点可以有多个父节点。
显然,一个简单的 BFS 搜索不足以解决这个问题。我知道存在一些算法来检测图中的循环。一种方法可能是检测所有循环,然后对于每个循环,检测路径是否有效。(即不反转方向)
还有其他人对解决此问题的方法有任何见解吗?
更新: 我遵循@Karussell 在评论中建议的方法。
诀窍是使用基于边缘的图而不是传统的基于顶点的图来模拟情况。比赛中提供的输入文件已经根据边方便地指定了,因此该文件可以很容易地用于构建基于边的图。
该程序使用两个重要的类:Road 和 Solver。Road 有两个整数字段,j1 和 j2。j1 代表源交汇点,j2 代表目标交汇点。每条道路都是单向的,这意味着您只能从 j1 行驶到 j2。每条道路还包括相邻道路的链接列表和父道路。Road 类还包括静态方法,用于在输入文件中使用的字符串和表示每个交叉点处的 A、B 和 C 点的整数索引之间进行转换。
对于输入文件中的每个条目,我们将两条 Roads 添加到 HashMap 中,两个路口之间的每个方向对应一个 Road。我们现在有一个在交叉点之间运行的所有道路的列表。我们只需要通过 A、B 和 C 开关将路口的道路连接在一起。如果一条道路在 Junction.A 结束,我们查找从 Junction.B 和 Junction.C 开始的道路并将这些道路添加为邻接。buildGraph() 函数返回目标交汇点 (j2) 为“1A”== 索引 0 的道路。
至此,我们的图就构建好了。为了找到最短路径,我只是使用 BFS 来遍历图形。我们不标记根,并从排队根的邻接开始。如果我们找到一条目标路口为“1A”(索引 0)的道路,那么我们就找到了通过起点的最短循环。一旦我们使用每个 Road 的 parent 属性重建路径,就可以根据问题的需要适当地设置开关。
感谢 Karussell 提出这种方法。如果您想以简短解释的形式将您的评论放在答案中,我会接受。也感谢@Origin。我必须承认我没有完全按照你的回答逻辑,但这当然不是说它不正确。如果有人使用您的解决方案解决了这个问题,我会非常有兴趣看到它。