博弈论经典算法(一)——对抗搜索与Alpha-Beta剪枝
前言
在一些复杂的博弈论题目中,每一轮操作都可能有许多决策,于是就会形成一棵庞大的博弈树。
而有一些博弈论题没有什么规律,针对这样的问题,我们就需要用一些十分玄学的算法。
例如对抗搜索。
对抗搜索简介
一、 对抗搜索的适用范围
在博弈论题目中,如果决策双方的获胜条件是截然相反的,即一方要求得分越高越好,另一方要求得分越低越好,这时我们就可以用上对抗搜索算法。
二、对抗搜索的主要思想
对抗搜索的核心思想就是\(dfs\)遍历一遍博弈树。
不难想到,如果博弈树非常庞大,在不加优化的情况下,对抗搜索的时间效率是十分低下的。
因此,我们就需要对对抗搜索进行一定的优化。
三、对抗搜索的优化
对抗搜索的优化一般来讲有两种:记忆化和 \(Alpha-Beta\)剪枝 。
不过需要注意,如果两个优化一起使用,很可能会产生化学反应出现一些奇妙的\(Bug\)(我已经亲身体验过了)。
对抗搜索优化一:记忆化
记忆化应该是搜索中一个比较常用的技巧。
一、大致思路
它的大致思路就是,对于当前的某一种状态,在求解后将结果记录下来,下一次再访问到时直接将存下来的结果返回即可。
二、模板
记忆化优化对抗搜索的伪代码如下:
inline int dfs(Status s,int Which)//Status记录当前状态,Which记录当前操作的选手,其中0号选手取Max,1号选手取Min
{
if(res[s]) return res[s];//如果之前已经求出过这个状态的结果,直接返回
if(IsEnd(s)) return GetVal(s);//如果当前状态已经为最终状态,就返回当前状态的分值
register int i,ans=Which?1e9:0;
extend(s);//扩展当前状态,并将新状态存储于NewStatus数组中,用NewStatusTotal记录新状态的数量
for(i=1;i<=NewStatusTotal;++i)//枚举从当前状态能够扩展到的新状态
ans=Which?min(ans,dfs(NewStatus[i],Which^1):max(ans,dfs(NewStatus[i],Which^1);//不断dfs,更新ans
return res[s]=ans;//将最终求解出的结果存储下来
}
对抗搜索优化二:\(Alpha-Beta\)剪枝
\(Alpha-Beta\)剪枝应该是对抗搜索一个比较巧妙的优化。
一、大致思路
如图是一棵博弈树:

假设第一个决策者的目的是取最大值,第二个决策者的目的是取最小值。
在搜索完根节点的两个子节点后,博弈树就会变成这样:

这时,我们再来看根节点的第三个子节点。
不难发现,在处理完第三个节点的第一个子节点之后,第三个节点的权值就会变成\(2\)。
因为第三个节点的目标是取最小值,因此最终第三个节点的权值必定小于等于\(2\)。
而根节点的目标是取最大值,且此时根节点的权值已经为\(3\)了。
也就是说,第三个节点对最终答案肯定是没有任何贡献的。
因此对于第三个节点的剩余两个状态,我们就无需继续搜索了,可以直接退出。
这就是传说中的\(Alpha-Beta\)剪枝了。
二、模板
\(Alpha-Beta\)剪枝优化对抗搜索的伪代码如下:
inline int dfs(Status s,int Alpha,int Beta,int Which)//Status记录当前状态,Which记录当前操作的选手,其中0号选手取Max,1号选手取Min
//Alpha存储较大值,Beta存储较小值
//如果当前节点是取Max的节点,则Alpha表示当前节点父亲的父亲的权值,Beta表示当前节点父亲的权值
//如果当前节点是取Min的节点,则Alpha表示当前节点父亲的权值,Beta表示当前节点父亲的父亲的权值
{
if(IsEnd(s)) return GetVal(s);//如果当前状态已经为最终状态,就返回当前状态的分值
register int i;
extend(s);//扩展当前状态,并将新状态存储于NewStatus数组中,用NewStatusTotal记录新状态的数量
for(i=1;i<=NewStatusTotal;++i)//枚举从当前状态能够扩展到的新状态
{
t=dfs(NewStatus,Alpha,Beta,Which^1);//求出当前枚举到的新状态的分值
(s.Which?Beta=min(Beta,t):Alpha=max(Alpha,t)); //如果当前节点取min,就更新Beta,否则更新Alpha
if(Alpha>=Beta) break;//如果Alpha≥Beta,就说明这个节点对最终答案没有贡献了,就结束搜索
}
return s.Which?Beta:Alpha;//返回相应值
}
后记
对抗搜索的核心内容差不多也就是这些。
但是,如果真正用起来,对抗搜索其实还是挺复杂的。
下面推荐一道例题:【BZOJ3106】[CQOI2013] 棋盘游戏
博弈论经典算法(一)——对抗搜索与Alpha-Beta剪枝的更多相关文章
- CCF(棋局评估)博弈论+对抗搜索+DFS
201803-4 棋局评估 这题主要使用对抗搜索,也就是每一步寻找可以下棋的位置,通过在这一步下棋看最后会取的什么样的分数. #include<iostream> #include< ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- 经典算法题每日演练——第十七题 Dijkstra算法
原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...
- 经典算法研究系列:二、Dijkstra 算法初探
July 二零一一年一月 本文主要参考:算法导论 第二版.维基百科. 一.Dijkstra 算法的介绍 Dijkstra 算法,又叫迪科斯彻算法(Dijkstra),算法解决的是有向图中单个源点到 ...
- 算法笔记--极大极小搜索及alpha-beta剪枝
参考1:https://www.zhihu.com/question/27221568 参考2:https://blog.csdn.net/hzk_cpp/article/details/792757 ...
- Python3入门机器学习经典算法与应用
<Python3入门机器学习经典算法与应用> 章节第1章 欢迎来到 Python3 玩转机器学习1-1 什么是机器学习1-2 课程涵盖的内容和理念1-3 课程所使用的主要技术栈第2章 机器 ...
- [经典算法题]寻找数组中第K大的数的方法总结
[经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26 字体:[大 中 小] 打印复制链接我要评论 今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...
- 经典算法mark
在平时找工作的时候,或多或少会遇到一些算法问题,很多都是比较经典或者网上已经流传很久的.只是我们没有接触过,所以不知道怎么解决. 在这儿,我自己总结一些我遇到的一些经典算法,给自己增加一点记忆,也给需 ...
- Python3入门机器学习经典算法与应用☝☝☝
Python3入门机器学习经典算法与应用 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 使用新版python3语言和流行的scikit-learn框架,算法与 ...
随机推荐
- Bigdecimal 比较equals与compareTo
原文链接:https://blog.csdn.net/jixinhuluwa/article/details/72626598 1.b.equals(BigDecimal.ZERO); 该方法存在的问 ...
- 一切从这里起始(左耳听风 ARTS 6号小组 week 1)
ARTS 具体要求: 1.每周至少做一个 leetcode 的算法题2.阅读并点评至少一篇英文技术文章3.学习至少一个技术技巧4.分享一篇有观点和思考的技术文章 1.Algorithm Two Sum ...
- 模型事件注意点,before_delete、after_delete、before_write、after_write、before_update、after_update、before_insert、after_insert
模型类支持before_delete.after_delete.before_write.after_write.before_update.after_update.before_insert.af ...
- Java中的生产者和消费者实例(多线程 等待唤醒机制)
1.什么是等待唤醒 我们实现的效果 创建生产者和消费者 对服装进行生产 和售卖 实现生产一个就消费一个 来观察线程的各种状态 下面是用到的方法: wait()方法:让一个线程进行等待 另外一个线程 ...
- 利用git reflog找回错误的重置
在开发中经常需要reset分支,如果在reset前没有记住分支指向的提交ID,想要重置回原来的提交恐怕大多数开发者是重新拉取远程版本库,再rebase分支.但如果连不上远程版本库或没有远程版本怎么办呢 ...
- 如何理解javascript中的同步和异步
javascript语言是一门“单线程”的语言,不像java语言,类继承Thread再来个thread.start就可以开辟一个线程,所以,javascript就像一条流水线,仅仅是一条流水线而已,要 ...
- HashMap遍历时的性能对比
使用KeySet和EntrySet遍历的差别 public static void main(String[] args) { HashMap<Integer, Integer> hasM ...
- Vue源码学习之数据初始化
首发地址:CJWbiu's Blog 在这里思考一个问题,使用Vue的时候需要在创建Vue实例时传入一个option,这里包含了我们定义的props.methods.data等.而在methods的方 ...
- Redis中的LRU淘汰策略分析
Redis作为缓存使用时,一些场景下要考虑内存的空间消耗问题.Redis会删除过期键以释放空间,过期键的删除策略有两种: 惰性删除:每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除 ...
- android 开发-ListView与ScrollView事件冲突处理(事件分发机制处理)
ListView和ScrollView都存在滚动的效果,所以一般不建议listView和scrollView进行嵌套使用,但有些需求则需要用到两者嵌套.在android的学习中学了一种事件分发处理机制 ...