算法笔记--极大极小搜索及alpha-beta剪枝
参考1:https://www.zhihu.com/question/27221568
参考2:https://blog.csdn.net/hzk_cpp/article/details/79275772
参考3:https://blog.csdn.net/BIT1120172185/article/details/80963609
极小极大搜索算法即minimax搜索算法
主要应用于零和博弈(非胜即负,如围棋,象棋,井子棋等),完全信息(玩家知道之前所有的步骤。象棋就是完全信息,因为玩家是交替着落子,且之前的步骤都能在棋盘上体现)
这个算法采用搜索算法递归实现,一层为先手,记为a, 一层为后手,记为b, 交替出现
对于最终局面,有一个分数(比如:先手胜分数为1, 平局分数为0,先手输分数为-1)
先手a想要让这个分数越大越好,后手b想要让这个分数越小越好,于是搜索到先手这一层,取最大返回,搜索到后手这一层,取最小返回
如下图:

于是伪代码为:
int MaxMin(position,int d)
{
int bestvalue,value;
if(game over) //检查游戏是否结束
return evaluation(p);// 游戏结束,返回估值
if(depth<=) //检查是否是叶子节点
return evaluation(p);//叶子节点,返回估值
if(max) //极大值点
bestvalue=-INFINTY;
else //极小值点
bestvalue=INFINTY;
for(each possibly move m)
{
MakeMove(m); //走棋
value=MaxMin(p,d-);
UnMakeMove(m); //恢复当前局面
if(max)
bestvalue=max(value,bestvalue);//取最大值
else
bestvalue=min(value,bestvalue);//取最小值
}
return bestvalue;
}
关于alpha-beta剪枝:
如果当前层为取最小,如果取最小后比上一层当前最大值还小,则不需要往下搜索,因为上一层根本不会选择当前节点往下搜,还有更好的选择
同理,如果当前层为取最大,如果取最大后比上一层当前最小值还大,则不需要往下搜索,因为上一层根本不会选择当前节点往下搜
具体推荐看最上面的知乎链接点赞最多的回答。
alpha-beta剪枝后的伪代码:
int AlphaBeta(nPlay,nAlpha,nBeta)
{
if(game over)
return Eveluation; //胜负已分,返回估值
if(nPly==)
return Eveluation; //叶子节点返回估值
if(Is Min Node) //判断 节点类型
{ // 极小值节点
for(each possible move m)
{
make move m; //生成新节点
score=AlphaBeta(nPly-,nAlpha,nBeta)//递归搜索子节点
unmake move m;//撤销搜索过的节点
if(score<nBeta)
{
nBeta=score;//取极小值
if(nAlpha>=nBeta)
return nAlpha;//alpha剪枝,抛弃后继节点
}
}
return nBeta;//返回最小值
}
else
{//取极大值的节点
for(each possible move m)
{
make move m; //生成新节点
score=AlphaBeta(nPly-,nAlpha,nBeta)//递归搜索子节点
unmake move m;//撤销搜索过的节点
if(score>nAlpha)
{
nAlpha=score;//取极小值
if(nAlpha>=nBeta)
return nBeta;//nBeta剪枝,抛弃后继节点
}
}
return nAlpha;//返回最小值
}
}
例题1:POJ - 1568
思路:井子棋下的步数小于等于4必平局
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<cstdio>
#include<iostream>
using namespace std;
#define fi first
#define se second
#define pi acos(-(long double)1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<long double, long double>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head char s[], mp[][];
int cnt = , ansx, ansy;
int check(char c) {
for (int i = ; i < ; i++) {
int a = ;
for (int j = ; j < ; j++) {
if(mp[i][j] == c) a++;
}
if(a == ) return ;
a = ;
for (int j = ; j < ; j++) {
if(mp[j][i] == c) a++;
}
if(a == ) return ;
}
int a = ;
for (int i = ; i < ; i++) if(mp[i][i] == c) a++;
if(a == ) return ;
a = ;
for (int i = ; i < ; i++) if(mp[i][-i] == c) a++;
if(a == ) return ;
return ;
}
int dfs(int step, int a, int b) {
if((cnt-step)% == ) {
int tmp = check('o');
if(tmp || step == ) return -tmp;
}
else {
int tmp = check('x');
if(tmp || step == ) return tmp;
}
if((cnt-step)% == ) {
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
if(mp[i][j] == '.') {
mp[i][j] = 'x';
int tmp = dfs(step-, a, b);
mp[i][j] = '.';
if(tmp >= a) {
if(step == cnt) {
ansx = i;
ansy = j;
}
a = tmp;
if(b <= a) return b;
}
}
}
}
return a;
}
else {
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
if(mp[i][j] == '.') {
mp[i][j] = 'o';
int tmp = dfs(step-, a, b);
mp[i][j] = '.';
if(tmp <= b) {
b = tmp;
if(a >= b) return a;
}
}
}
}
return b;
}
}
int main() {
while(~scanf("%s", s)) {
if(s[] == '$') return ;
for (int i = ; i < ; i++) scanf("%s", mp[i]);
cnt = ;
for (int i = ; i < ; i++) for (int j = ; j < ; j++) if(mp[i][j] == '.') cnt++;
if(cnt >= ) {
printf("#####\n");
continue;
}
int ans = dfs(cnt, -, );
if(ans == ) printf("(%d,%d)\n", ansx, ansy);
else printf("#####\n");
}
return ;
}
例题2:POJ - 1085
思路1:记忆化搜索dp, dp[s]表示从状态s出发先手所能获得的最大利益。
特点:空间大,时间短
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<long double, long double>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head int line[][] = {{, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }};
int tri[][] = {{, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }};
int num[][];
int dp[<<];
int count(int s) {
int cnt = ;
for (int i = ; i < ; i++) if((s&(<<tri[i][])) && (s&(<<tri[i][])) && (s&(<<tri[i][]))) cnt++;
return cnt;
}
int dfs(int s) {
if(~dp[s]) return dp[s];
if(__builtin_popcount(s) == ) return ;
int prenum = count(s);
int ans = INT_MIN;
for(int i = ; i < ; i++) {
if(!(s&(<<i))) {
int nownum = count(s|(<<i));
if(nownum == prenum) {
ans = max(ans, -dfs(s|<<i));
}
else {
ans = max(ans, nownum - prenum + dfs(s|<<i));
}
}
}
return dp[s] = ans;
}
int main() {
int T, m, u, v;
for (int i = ; i < ; i++) num[line[i][]][line[i][]] = num[line[i][]][line[i][]] = i;
mem(dp, -);
scanf("%d", &T);
for(int cs = ; cs <= T; cs++) {
int st = ;
scanf("%d", &m);
int prenum = , nownum = ;
int cnt = , step = ;
for (int i = ; i <= m; i++) {
scanf("%d %d", &u, &v);
st |= <<num[u][v];
nownum = count(st);
if(nownum > prenum) {
if(step% == ) cnt += nownum - prenum;
else cnt -= nownum - prenum;
}
else step++;
prenum = nownum;
}
if(step% == ) cnt += dfs(st);
else cnt -= dfs(st);
if(cnt > ) printf("Game %d: A wins.\n", cs);
else printf("Game %d: B wins.\n", cs);
}
return ;
}
思路2:极大极小搜索+alpha-beta剪枝
特点:空间小,时间长
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
using namespace std;
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<long double, long double>
#define mem(a, b) memset(a, b, sizeof(a))
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
//head int line[][] = {{, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }, {, }};
int tri[][] = {{, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }, {, , }};
int num[][];
int count(int s) {
int cnt = ;
for (int i = ; i < ; i++) if((s&(<<tri[i][])) && (s&(<<tri[i][])) && (s&(<<tri[i][]))) cnt++;
return cnt;
}
int dfs(int s, int a, int b, int alpha, int beta, int step) {
if(__builtin_popcount(s) == ) {
if(a > b) return ;
else return -;
}
int prenum = count(s);
for(int i = ; i < ; i++) {
if(!(s&(<<i))) {
int nownum = count(s|(<<i));
if(nownum == prenum) {
if(step% == ) {
int tmp = dfs(s|<<i, a, b, alpha, beta, step+);
if(tmp >= alpha) {
alpha = tmp;
if(alpha >= beta) return alpha;
}
}
else {
int tmp = dfs(s|<<i, a, b, alpha, beta, step+);
if(tmp <= beta) {
beta = tmp;
if(alpha >= beta) return beta;
}
}
}
else {
if(step% == ) {
int tmp = dfs(s|<<i, a+nownum-prenum, b, alpha, beta, step);
if(tmp >= alpha) {
alpha = tmp;
if(alpha >= beta) return alpha;
}
}
else {
int tmp = dfs(s|<<i, a, b+nownum-prenum, alpha, beta, step);
if(tmp <= beta) {
beta = tmp;
if(alpha >= beta) return beta;
}
}
}
}
}
if(step% == ) return alpha;
else return beta;
}
int main() {
int T, m, u, v;
for (int i = ; i < ; i++) num[line[i][]][line[i][]] = num[line[i][]][line[i][]] = i;
scanf("%d", &T);
for(int cs = ; cs <= T; cs++) {
int st = ;
scanf("%d", &m);
int prenum = , nownum = ;
int step = , a = , b = ;
for (int i = ; i <= m; i++) {
scanf("%d %d", &u, &v);
st |= <<num[u][v];
nownum = count(st);
if(nownum > prenum) {
if(step% == ) a += nownum - prenum;
else b += nownum - prenum;
}
else step++;
prenum = nownum;
}
int cnt = dfs(st, a, b, -, , step);
if(cnt > ) printf("Game %d: A wins.\n", cs);
else printf("Game %d: B wins.\n", cs);
}
return ;
}
算法笔记--极大极小搜索及alpha-beta剪枝的更多相关文章
- POJ 1568 极大极小搜索 + alpha-beta剪枝
极小极大搜索 的个人理解(alpha-beta剪枝) 主要算法依据就是根据极大极小搜索实现的. 苦逼的是,查了两个晚上的错,原来最终是判断函数写错了..瞬间吐血! ps. 据说加一句 if sum & ...
- 极大极小搜索思想+(α/β)减枝 【转自-----https://blog.csdn.net/hzk_cpp/article/details/79275772】
极大极小搜索,即minimax搜索算法,专门用来做博弈论的问题的暴力. 多被称为对抗搜索算法. 这个搜索算法的基本思想就是分两层,一层是先手,记为a,还有一层是后手,记为b. 这个搜索是认为这a与b的 ...
- 软件发布版本区别介绍-Alpha,Beta,RC,Release
Alpha: Alpha是内部测试版,一般不向外部发布,会有很多Bug.除非你也是测试人员,否则不建议使用. 是希腊字母的第一位,表示最初级的版本 alpha就是α,beta就是β alpha版就是比 ...
- 算法笔记--数位dp
算法笔记 这个博客写的不错:http://blog.csdn.net/wust_zzwh/article/details/52100392 数位dp的精髓是不同情况下sta变量的设置. 模板: ]; ...
- 算法笔记之KMP算法
本文是<算法笔记>KMP算法章节的阅读笔记,文中主要内容来源于<算法笔记>.本文主要介绍了next数组.KMP算法及其应用以及对KMP算法的优化. KMP算法主要用于解决字符串 ...
- [转载]SharePoint 2013搜索学习笔记之搜索构架简单概述
Sharepoint搜索引擎主要由6种组件构成,他们分别是爬网组件,内容处理组件,分析处理组件,索引组件,查询处理组件,搜索管理组件.可以将这6种组件分别部署到Sharepoint场内的多个服务器上, ...
- poj 1568 Find the Winning Move 极大极小搜索
思路:用极大极小搜索解决这样的问题很方便!! 代码如下: #include <cstdio> #include <algorithm> #define inf 10000000 ...
- 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)
Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...
- 软工+C(2017第4期) Alpha/Beta换人
// 上一篇:超链接 // 下一篇:工具和结构化 注:在一次软件工程讨论课程进度设计的过程中,出现了这个关于 Alpha/Beta换人机制的讨论,这个机制在不同学校有不同的实施,本篇积累各方观点,持续 ...
随机推荐
- mysql8.0发布新特性
2018年4月21日 14:36:42 https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-11.html#mysqld-8-0-11-b ...
- 在CentOS 7.6上安装VNC Server
停止并禁用防火墙 systemctl stop firewalld.service systemctl disable firewalld.service 安装vnxserver yum instal ...
- 《Redis 优化》
一:管道技术 - 由于 redis 和 客户端是使用 TCP 连接的,那么在使用中就会产生往返耗时. - 虽然可能单条影响并不大,但是如果执行较多的命令会对性能产生影响. - 使用管道原理和 keep ...
- Java学习-049-正则工具类
自去年九月份决定再次入学和职业资格进阶,开始备战二者考试至今,以及当下进行中的职称申请,犹如孤独的狼,不断前行在路上,而今凡凡总总的已历8月... 不感慨了,如下为一园友需要的正则工具类,直接上码: ...
- 阻塞队列(BlockingQueue)
阻塞队列是 java.util.concurrent 包提供的一个类,该类提供了多线程中通过队列实现安全高效的数据处理的功能. 所谓阻塞队列,是在普通队列基础上实现了阻塞线程的功能: 队列为空时,获取 ...
- SpringBoot-@RequestParam
Request参数 在访问各种各样网站时,经常会发现网站的URL的最后一部分形如:?xxxx=yyyy&zzzz=wwww.这就是HTTP协议中的Request参数,它有什么用呢?先来看一个例 ...
- mixpanel umeng talkingdata
市面上可以比较容易的接触到的实时大数据用户行为分析系统有很多,比如国外有著名的Mixpanel.Localytics.Google,国内有TalkingData.这些公司都提供基于云的大数据分析系统, ...
- 合作开发工具——freeze和pipreqs
以后在合作开发的过程中,难免会用到别人开发到一半或者将自己开发的项目交给别人,在转交项目的时候需要让别人知道本项目中用到了哪些模块,这时可以用到一条命令来帮助我们. pip3 freeze # 获取环 ...
- PHP on CentOS (LAMP) and wordpress
http://php.net/manual/zh/install.windows.php https://www.unixmen.com/install-wordpress-centos-7-linu ...
- rabbitmq延迟队列demo
1. demo详解 1.1 工程结构: 1.2 pom 定义jar包依赖的版本.版本很重要,rabbit依赖spring,两者必须相一致,否则报错: <properties> <sp ...