C++抽象编程——回溯算法(1)——迷宫问题

这部分内容其实是递归策略的一部分,但是里面涉及到了一些面向对象的知识,所以我就先总结了面向对象那一部分。这部分内容不得不说还是很有意思的。
##迷宫问题
曾经在希腊神话时代,地中海的克里特岛被一个名叫米诺斯的暴君统治了(这是一头公牛头和一个人的身体的可怕的野兽)。米诺斯不时要求雅典市青年男女的形式致敬,选中的年轻人将祭祀到牛头人。 为了容纳它,米诺斯迫使他的仆人戴德洛斯(后来通过建造一套翅膀逃离的工程天才)在克诺索斯建造一个巨大的地下迷宫。 来自雅典的年轻祭品将被引导入迷宫,在那里他们只有在被牛头人吃掉之前找到出路。 这场悲剧持续到雅典的忒修斯自发成为祭品之一。 根据米诺斯的女儿阿里亚德的建议,忒修斯用剑和一串绳子进了迷宫。 在杀了怪物之后,忒修斯在他走过的同时,松了一口气,找到了回到出口的路。
下面是引用的原文;

Once upon a time, in the days of Greek mythology, the Mediterranean island of Crete was ruled by a tyrannical king named Minos. From time to time, Minos demanded tribute from the city of Athens in the form of young men and women,whom he would sacrifice to the Minotaur, a fearsome beast with the head of a bull and the body of a man. To house this deadly creature, Minos forced his servant Daedelus (the engineering genius who later escaped by constructing a set of wings) to build a vast underground labyrinth at Knossos. The young sacrifices from Athens would be led into the labyrinth, where they would be eaten by the Minotaur before they could find their way out. This tragedy continued until young Theseus of Athens volunteered to be one of the sacrifices. Following the advice of Minos’s daughter Ariadne, Theseus entered the labyrinth with a sword and a ball of string.After slaying the monster, Theseus was able to find his way back to the exit by unwinding the string as he went along.

##右手法则
Ariadne从迷宫中逃脱的策略是一种算法,但是并不是所有被困在迷宫中的人都很幸运地拥有一串绳子。 幸运的是,还有其他解决迷宫的策略。在这些策略中,最着名的方法称为右手法则,可以用以下伪代码形式表示:
Put your right hand against a wall.
while (you have not yet escaped from the maze) {
Walk forward keeping your right hand on a wall.
}
简言之,就是把手放在右侧的墙上,沿着这面墙走,当你没能走出墙的时候,继续走直到你走出去为止。为了可视化右手法则的操作,想象Ariadne已经成功地击败了牛头怪,现在正站在图中的位置,用希腊字母Θ(Θ)表示:
C++抽象编程——回溯算法(1)——迷宫问题_第1张图片
如果Ariadne把右手放在墙上,然后从那里按照右手规则,他将沿着图中虚线所示的路线前进:
C++抽象编程——回溯算法(1)——迷宫问题_第2张图片
不幸的是,右手规则实际上并不适用与每个迷宫。 如果有一个包围起始位置的循环,那么Ariadne可以被困在无限循环中,如以下简单的迷宫所示:
C++抽象编程——回溯算法(1)——迷宫问题_第3张图片

寻找递归的方法

由于其伪代码形式的while循环解释的很清楚,右手规则是一个迭代策略。然而,我们也可以从递归的角度思考解决迷宫的过程。要这样做,你必须采取不同的心态。在找到一条完整的路径方面你不能再想到这个问题。 相反,你的目标是找到简化问题的递归观点,一步一步。 简化之后,可以使用相同的过程来解决每个结果的子问题。我们回到右手法则插图中所示的迷宫的初始配置。把自己放在Ariadne的地方。 从初始配置,我们有三个选择,如下图中的箭头所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCPj3WdE-1573803195314)(http://i.imgur.com/Hznk4bM.png)]
出口(如果有的话)必须沿着这些路径之一。 此外,如果选择正确的方向,毫无疑问这将会更接近解决方案。因此,迷宫沿着这条路线变得更简单,这是递归解决方案的关键。这一观察结果表明了必要的递归观点。 当且仅当可以解决下图所示的至少一个新的迷宫时,原来的迷宫有一个解决方案。每个图中的×标记原始的起始位置(即你走的第一步),并且对于任何递归解决方案是非限制性的(比如你可以选择走3个方向),因为最优解决方案永远不需要回来这个位置(因为如果这三个方向的选择最终都要回来这个位置,说明这个迷宫没有出口,还有就是最优的解决方案永远不会往走过的方向再走一次,一定会一直走出去)。
因此,当且仅当可以解决下图所示的至少一个新的迷宫时,原来的迷宫有一个解决方案,迷宫的递归分解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohYf9bV8-1573803195314)(http://i.imgur.com/lYAGPAP.png)]
通过查看图中的迷宫,至少从全局的优势来看,很容易看出,标注为(a)和(c)的子贴图代表死路径,唯一的解决方案从方向开始如(b)所示。但是,如果你在递归地思考,则无需对解决方案进行分析。因为你已经将问题分解为更简单的实例(至少我们把问题分解成了三个方向上)。你需要做的是依靠递归的力量来解决个别的子问题。但是你仍然必须确定一组simple case,以便递归可以终止,但困难的工作已经完成。
##找到simple case
什么构成迷宫的simple case? 一种可能性是你可能已经站在迷宫之外了。 如果是这样,你就完成了。显然,这种情况是一个简单的例子。然而,还有另一种可能性。 你也可能到达一个盲路,你已经用尽了你的路径 例如,如果您尝试通过向北移动来解决示例迷宫,然后继续沿着该路径执行递归调用,则最终将会尝试解决以下迷宫:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yplTL8Sr-1573803195314)(http://i.imgur.com/j6G1XiK.png)]
在这一点上,你已经用尽了空间。来自新位置的每条路径都被标记或被墙壁阻挡,所以,这个条路径上没有解决方案。 因此,迷宫问题具有第二简单的情况,其中来自当前正方形的每个方向都标记或被墙壁阻挡。
如果在考虑可能的运动方向的情况下,不考虑标记的正方形,则可以更方便地对递归算法进行编码,然后继续执行这些路径上的递归调用。如果在过程开始时检查以查看当前路径是否已标记,则可以终止该点的递归。毕竟,如果你发现自己位于已经标记的路径上,你必须回到你的走过的路径,这意味着最佳解决方案一定在其他方向(换言之,这一定不是最佳路径)。
因此,这个问题的两个simple如下:

  • 如果你已经处于迷宫的外部,说明问题已经被解决,就无需递归(If the current square is outside the maze, the maze is solved
  • 如果当前的方块(就是X所示的部分)被标记,至少沿着你目前选择的路径,迷宫是无法解决的。(If the current square is marked, the maze is unsolvable, at least along the path you’ve chosen so far.

关于这个问题的编码,我下一篇再提,由于里面涉及的以前的内容,所以我们提前做一些准备

你可能感兴趣的:(抽象编程(C++),C++学习与基础算法)