语言回溯法马周游问题_回溯算法与游戏:N皇后、游戏树

本文介绍了使用回溯法解决经典的N-皇后问题,并探讨了游戏树的概念。在N-皇后问题中,通过避免在同一列、对角线上放置皇后来寻找解决方案。游戏树则描述了在对抗游戏中,双方玩家如何根据极大极小原则进行决策,以达到最优状态。文章阐述了如何判断游戏状态的好坏,并在决策过程中应用回溯算法。
摘要由CSDN通过智能技术生成

预计阅读 15min

 溯沿曲处疑山尽,行转通时觉水遥。

 ------黄廷用

“溯者,水欲下、違之而上也 。”回溯算法在搜索中寻找问题的解,如果发现走不通就返回之前的某个状态,因此而得名 。回溯算法使用满足一定限制的递归结构,通常用来解决多阶段的决策问题。本文选取了两个经典的回溯算法应用案例:
  • N-皇后问题

  • 游戏树


  (一)N皇后问题 有一个NxN的棋盘,你要在棋盘上放置N个女王,你需要找出所有的满足下列条件的摆棋方案:任意两个女王不在同一行或者同一列, 也不能在任意一条对角线上, 对角线不只是两条主副对角线。  f1afb9544949ce7c9f5354a949f975f7.png 思路:  每放置一个皇后,就将其能够攻击的区域进行标记,然后放置下一个皇后,依次类推……; 此问题难点在于如何把控递归函数的返回条件,一种条件是N个皇后放置完成后,返回成功,一种条件是该行中已经没有可以放置的位置,此时返回失败,需要重新放置。 所谓的“重新放置”指的并不是将所有皇后清除重新来过,而是只返回上一层,将上一个导致本次放置失败的皇后进行清除,然后重新更新其位置,通过逐级放置、或逐级回溯可以达到遍历所有情况找到所有解。 我们用一个数组Q【0...N-1】来储存第一行到第N行女王的放置位置。 Q数组的初始值均为-1,count是个全局变量,用来计数,初始为0。 伪代码如下,在主程序调用PlaceQueens(Q[0...n-1], 0,count)即可
PlaceQueens(Q[0...n-1], r,count):       if r==n              print Q[0...n-1]              count=count+1        else               for  j                       legal                       for i                                if(Q[i]==j or Q[i]==j+r-i or Q[i]==j-r+i)                                               legal                        if legal                                    Q[r]                                    PlaceQueens(Q[0...n-1], r+1)                                                                                
注意Q数组在运行过程中只有当前行以前的行信息是有用的,因此不必在每次调用结束后复原Q【j】的值。在此指出, if(Q[i]==j or Q[i]==j+r-i or Q[i]==j-r+i) 是本题关键
  • Q[i]==j 对应了同一列

  • Q[i]==j+r-i 也可写为Q[i]+i==j+r  对应了在同一条右倾的对角线上

  • Q[i]==j-r+i  也可写为Q[i]-i==j-r    对应了在同一条左倾的对角线上

我们可以用递归树来解释运行的过程。这棵树上的每一个节点都是一个子问题,对应了一个合法的部分解。 这棵树上的边对应了一次递归函数调用。叶子结点对应了没办法继续下去的部分解,要么是N个王后都安排好了位置,要么是没有合法的位置在安排下一个王后。实际上,回溯算法对应了对这颗递归树的深度优先搜索(点击查看)。 cdc1fae42d4d7349790f58b962a94d14.png
(二)游戏树(极大极小树) 概述:
  • 在玩对抗游戏时,通常假设双方都是顶级高手。因此默认敌我双方都能审时度势,作出最有利于自己的决策。

  • 在轮到我方做决策时,我们希望最大化自己的收益,因此称为极大层;

  • 在轮到敌方做决策时,他们的利益最大化就是我方利益最小化,因此称为极小层。

  • 极大极小层交替出现,因此称为极大极小树(游戏树)。

  • 每个决策都有一个收益值,我们会在所有可选的方案中选择收益值最大的方案。相反地,对方会选择让我方收益值最小的方案。

看一个例子:
有一个NxN的棋盘,有甲乙两人对决,两人均有N个棋子,甲的棋子位于棋盘左侧边缘,乙的棋子位于棋盘上侧边缘。甲的棋子每次只能向右移动一格,或者在右侧挨着一个乙的棋子时可以越过乙的棋子向右移动两个。乙的 棋子每次只能向下移动一格,或者在下方挨着一个甲的棋子时可以越过甲的棋子向下移动两格。 所有的棋子不能重叠。先把所有棋子移到另一侧棋盘边缘者胜。
游戏过程如下图: 38274122785c4fad47fc917131e72875.png 存在一个简单的回溯算法来教你玩这个游戏---或者任意的不存在随机和隐藏信息的且可以在有限步内结束的双人游戏。 游戏的状态由每个棋子的位置以及当前要下棋的玩家身份组成。这些状态可以转换成一个游戏树,状态x若可以转换成转态y ,则其对应的节点之间存在一条边。游戏树的根节点是游戏的初始状态。每一个从根节点到叶子节点的路径就是一个完整的游戏. b6ded8fbef1fa16126cca38b14c8cfaf.png我们遍历这个游戏树,并且按照下面递归地定义一个游戏状态是“好”还是坏 (好 和 坏 即为上文提到的收益值,也可用1,-1 ):
  •  如果当前的玩家已经赢了, 或者他可以把游戏转态移动到了对对手玩家来说是坏的状态,那么当前游戏转态就是好的。

  • 如果当前的玩家已经输了,或者他任何的操作都会把游戏状态移动到对对手玩家来说是好的状态,则当前游戏状态就是坏的。

等价的,在一个游戏树里:
  • 如果非叶子结点至少有一个坏的孩子节点,它就是好的节点;

  • 如果非叶子结点所有的孩子节点都是好的,他就是坏的节点;

任何一个玩家如果发现当前游戏处于好的状态,他就可能赢,即使对手是一个 完美的玩家。相反的, 如果玩家从一个坏的状态开始,只有对手出现失误,他才可能赢。这一递归定义由 Ernst Zermelo 在1913年提出。 d63624148f93c91008f29668f98d14ac.png以上的算法实际上是对游戏树的深度优先遍历。从算法中看出,当我们不能直接得到当前状态的收益时,我们会把下一层的收益最大值作为本层收益。此程序可以判断当前的状态是好还是坏。当我们发现游戏处于对我坏的状态,也不要放弃,没准对方手滑了呢。总结:回溯算法是一种暴力穷举算法,考虑了所有的可能性。我们可以对其进行优化,比如采用动态规划(点击查看),或者采用合适的剪枝策略。 reference: <> by Jeff Erickson
最后小声说一下,公众号菜单里加了资料领取这一栏。今天也有更新C++语言学习资料呦。可以来戳~~

2564e80b18e928274006dbf2a2d789f8.png

ab3b1bdbf586a72573ea95f382068f3e.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值