对抗搜索 学习笔记

news2024/11/27 17:56:01

先来看一道有意思题

situation

大意:

两个人玩井字棋,要求把所有位置填满后结束游戏。一方希望两者的连起来的线之差最多,一方希望最少。现在给定初始局面(已经存在一些棋子)以及先手,求最后的两者的连起来的线之差。

这就是一道对抗搜索的题目

什么是对抗搜索?简单来说,博弈双方对竞争相反的利益,一方使其最大化,一方使其最小化,那么我们就可以通过搜索来探索最终状态

min-max对抗搜索

假设我们已经有了一颗博弈树:
 

 如果我方先手且我方以最大化利益为目标,那么单数层就是我方的决策层,双数层就是对方的。那么在单数层的节点,它会选取利益最大化的子局面,反之亦然。

现在我们从最底层开始向上,D在单数层,它会选取最大的利益,所以它的利益为2。B就会在D和E的利益之间选取最小的...依次类推,最后我们就可以得到初始局面对应的利益了。

但是当博弈规模比较大的时候,搜索规模也会爆炸,就要考虑剪枝。

这里引入:

Alpha_Beta对抗搜索

我们限定一个利益区间【a,b】

α Alpha is the maximum lower bound of possible solutions 

对于一个追求max利益的节点P,它的所有子节点都是追求min利益,会将收益尽可能降低,那么P就会在所有尽可能低的收益里选最高的,也就是α了。

β Beta is the minimum upper bound of possible solutions

β同理。是在所有尽可能高的收益里取低

换句话说,α和β其实都是对应最差情况下的收益。

那么α会如何更新?显然被每一个子局面的最差情况更新,也就是子局面的β,同理β就会被子局面的α更新。

初始时a=-inf,b=inf(显然)

不难发现,对于一个追求最大收益的节点,如果它的子节点(都追求最小化收益)存在一个子局面,能够获得比a更小的收益,我们就可以剪掉对应子树,不然利益一定不会在区间范围内

同理,对于一个追求最小化收益的节点,如果子节点存在一个可以获得比b更大的收益,就要剪掉

否则我们的子节点就可以更新当前区间。区间不断缩小,最后确定收益值。


另外这里再考虑一下遍历的问题。显然一个父节点的区间端点要再子节点的端点里取极值,那么层序遍历(从下往上)就是不合理的,因为这样就还要去记录子节点的值。更好的是采用先序/后序遍历,就可以在搜索的时候完成更新了。

题解

最后讲回这题。

因为数据规模非常小,直接做min-max对抗搜索就可以了。对于每一个确定的局面我们可以很轻松就确定其对应的收益,那么剩余情况就暴力dfs就好了。然后为了区分情况方便记忆化,可以哈希一下。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int inf=0x3f3f3f3f;
const ll N=3e5+10;
string s;

struct ty//局面 
{
	char tr[5][5];
	bool judge(ll id,ll op)
	{
		if(op==0)//横排
		{
			for(int i=2;i<=3;++i)
			{
				if(tr[id][i]!=tr[id][i-1]) return 0;	
			}	
			return 1;
		} 
		else if(op==1)//竖排 
		{
			for(int j=2;j<=3;++j)
			{
				if(tr[j][id]!=tr[j-1][id]) return 0;
			}
			return 1;
		}
		else if(op==2)//斜着 
		{
			if(id==1)
			{
				for(int i=2;i<=3;++i)
				{
					if(tr[i][i]!=tr[i-1][i-1]) return 0;
				}
				return 1;
			} 
			else if(id==2)
			{
				for(int i=2;i<=3;++i)
				{
					if(tr[i][4-i]!=tr[i-1][5-i]) return 0;
				}
				return 1;
			}
		}
	}
	ll get_haxi()
	{
		ll haxi=0;//哈希值 
		for(int i=1;i<=3;++i)//三进制表示 
		{
			for(int j=1;j<=3;++j)
			{
				haxi*=3;
				if(tr[i][j]=='O') haxi++;
				else if(tr[i][j]=='X') haxi+=2;
			}
		}
		return haxi;
	}
	int get()
	{
		int ans=0;
		for(int i=1;i<=3;++i)
		{
			if(judge(i,0)==0) continue;
			else if(tr[i][1]=='O') ans++;
			else ans--;
		}
		for(int i=1;i<=3;++i)
		{
			if(judge(i,1)==0) continue;
			else if(tr[1][i]=='O') ans++;
			else ans--;
		}
		if(judge(1,2))
		{
			if(tr[1][1]=='O') ans++;
			else ans--;
		}
		if(judge(2,2))
		{
			if(tr[2][2]=='O') ans++;
			else ans--;
		}
		return ans;
	}
	bool jd()
	{
		for(int i=1;i<=3;++i)
		{
			for(int j=1;j<=3;++j)
			{
				if(tr[i][j]=='.') return 1;
			}
		}
		return 0;
	}
}; 
int dp[N][3];
int dfs(ty now,int op)
{
	int haxi=now.get_haxi();
	if(dp[haxi][op]!=-inf) return dp[haxi][op];
	if(!now.jd()) return now.get();//直接出结果
	if(op)//取max,最大化收益 
	{
		for(int i=1;i<=3;++i)
		{
			for(int j=1;j<=3;++j)
			{
				if(now.tr[i][j]=='.')//开始 
				{
					ty nex=now;
					nex.tr[i][j]='O';
					dp[haxi][op]=max(dp[haxi][op],dfs(nex,0));
				}	
			}	
		}	
	} 
	else
	{
		dp[haxi][op]=inf;
		for(int i=1;i<=3;++i)
		{
			for(int j=1;j<=3;++j)
			{
				if(now.tr[i][j]=='.')//开始 
				{
					ty nex=now;
					nex.tr[i][j]='X';
					dp[haxi][op]=min(dp[haxi][op],dfs(nex,1));
				}	
			}
		}
	}
	return dp[haxi][op];
}
void init()
{
	for(int i=0;i<=100000;++i)
	{
		for(int j=0;j<=1;++j)
		{
			dp[i][j]=-inf;
		}
	}
}
void solve()
{
	ll op;
	cin>>op;
	ty nd;
	for(int i=1;i<=3;++i)
	{
		for(int j=1;j<=3;++j)
		{
			cin>>nd.tr[i][j];
		}
	}
	cout<<dfs(nd,op)<<endl;
//    ty nd;
//    for(int i=1;i<=3;++i)
//    {
//    	for(int j=1;j<=3;++j)
//    	{
//    		nd.tr[i][j]='X';
//		}
//	}
//	cout<<nd.get()<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	init();
	ll t;cin>>t;while(t--)
	solve();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP FICO预制凭证界面隐藏过账按钮

会计凭证一旦过账了就不能再进行修改&#xff0c;但其也提供了类似国内财务软件同样的预制功能&#xff0c;预制凭证过账之前不会更新会计系统。预制凭证虽然不更新科目余额&#xff0c;但同样会生成凭证编号&#xff0c;其凭证内容可以随意更改&#xff0c;也可以删除。一旦过…

bios设置u盘启动重装系统教程

​如今&#xff0c;大部分人都会采用U盘启动盘装系统&#xff0c;而使用U盘装系统之前&#xff0c;有一个很重要的步骤&#xff0c;那就是设置U盘启动。大部分电脑都可以直接通过u盘启动快捷键来选择U盘启动&#xff0c;少部分电脑只能通过bios设置u盘为第一启动项。那么&#…

支付宝的架构

自 2008 年双 11 以来&#xff0c;在每年双 11 超大规模流量的冲击上&#xff0c;蚂蚁金服都会不断突破现有技术的极限。2010 年双 11 的支付峰值为 2 万笔/分钟&#xff0c;到 2017 年双 11 时这个数字变为了 25.6 万笔/秒。 2018 年双 11 的支付峰值为 48 万笔/秒&#xff0c…

log4j2的使用

Log4j2的使用 概述 Apache Log4j 2是对Log4j的升级版&#xff0c;参考了logback的一些优秀的设计&#xff0c;并且修复了一些问题&#xff0c;因此带来了一些重大的提升&#xff0c;主要有&#xff1a; 异常处理&#xff0c;在logback中&#xff0c;Appender中的异常不会被应…

若依 ruoyi 配置多数据源 生成代码 导出代码

本文相关库说明&#xff1a;vue&#xff08;若依自带库&#xff09;db_game多数据源从库1db_paystore 多数据源从库2多数据源的情况下&#xff0c;想生成其他从库下数据库表对应的代码&#xff0c;但是若依自带的导入表中 是不会查询到从库各数据表信息的(只查询到若依框架对应…

我是这样解决 HBuilderX 安卓基座安装失败的问题

本文简介 点赞 关注 收藏 学会了 记录一个在使用 HBuilderX 开发 App 时遇到的问题。 同步资源失败&#xff0c;未得到同步资源的授权&#xff0c;请停止运行后重新运行&#xff0c;并注意手机上的授权提示 出现这个问题的原因是我把手机的 HBuilder App 给删掉了&#xff…

【自学Java】Java多维数组

Java多维数组 Java多维数组教程 Java 语言 中有 一维数组&#xff0c;也会有多维数组。如果有一个二维数组&#xff0c;那么数组的每个元素将会是一维数组&#xff0c;而不是单纯的元素。如果是一个多维数组&#xff0c;那么每个位置上面对应的是 纬度 - 1 的数组。 因为在平…

从官方文档学习Rabbit与SpringAMQP-乱版

本文也是笔者一直没有去详细学习的一个重要知识点MQ&#xff0c;也是架构中非常重要的一个中间件。 主要从Rabbit官网于Spring AMQP官方文档的角度去详细学习MQ 官方文档 Rabbit Spring AMQP 学习结果 测试项目地址 导读 本文主要从以下两个角度去学习MQ 一、RabbitMQ 官…

【nodejs】模块的加载机制

1、优先从缓存中加载 模块在第一次加载后会被缓存。意味着多次调用require()不会导致模块的代码被执行多次 注意&#xff1a;不论是内置模块、用户自定义模块、还是第三方模块&#xff0c;它们都会优先从缓存中加载&#xff0c;从而提高模块的加载效率。 &#xff08;1&#x…

【求证】 网上配镜靠谱吗?

肯定也有不少的小伙伴 看着网上琳琅满目的“明星同款”眼镜 心动不已 盘算着给自己弄一副“性价比” 最高“明星同款”那么网上配镜究竟靠不靠谱&#xff1f; 潍坊眼科医院眼健康管理中心提醒您 网上配镜需谨慎 网络平台的价格优势非常明显&#xff0c;部分销量高的店铺&#x…

二叉树基础计算

题目专题二叉树节点个数二叉树叶子节点个数二叉树第k 层节点个数二叉树查找值为x 的节点一共有这么几个题目&#xff0c;主要是用来表现题目的性质 分别是求 二叉树节点个数 二叉树叶子节点个数 二叉树第k 层节点个数 二叉树查找值为x 的节点 如果我使用的还是这个二叉树 其…

【linux】基础IO

文章目录一、复习文件相关知识二、复习C文件相关操作1、复习知识点2、复习操作三、文件的系统调用接口1、open2、write3、read小结四、文件描述符1、初步认识2、两个问题知识点3、文件描述符的分配规则五、重定向1、 dup2函数2、myshell里面实现重定向功能3、知识点六、如何理解…

【C++】stack、queue和deque

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;stack 的…

密集单目 SLAM 的概率体积融合

点击进入—>3D视觉工坊学习交流群笔者简述&#xff1a;这篇论文主要还是在于深度估计这块&#xff0c;深度估计由于硬件设备的不同是有很多方法的&#xff0c;双目&#xff0c;RGBD&#xff0c;激光雷达&#xff0c;单目&#xff0c;其中最难大概就是单目了。在该论文中作者…

Flutter不常用组件(四)

Offstage 创建一个在视觉上隐藏其子项的小部件。隐藏后不占空间。 该组件有以下几个属性&#xff1a; Key? key&#xff1a;标识键bool offstage true&#xff1a;是否隐藏。默认为trueWidget? child&#xff1a;子组件 Center(child: Column(mainAxisAlignment: MainAx…

【设计篇】36 # 如何理解可视化设计原则?

说明 【跟月影学可视化】学习笔记。 可视化设计的四个基本原则 基本原则一&#xff1a;简单清晰原则 我们可以看下面一张图&#xff1a;国际茶叶委员会制作的全球茶叶消费排行榜图表&#xff0c;目的是想说明喝茶最多的不是中国人 我们可以用更简单的直方图方式去表现 基本…

c++11 标准模板(STL)(std::deque)(九)

定义于头文件 <deque> std::deque 修改器 移除末元素 std::deque<T,Allocator>::pop_back void pop_back(); 移除容器的最末元素。 在空容器上调用 pop_back 是未定义的。 指向被擦除元素的迭代器和引用被非法化。尾后迭代器是否被非法化是未指定的。其他迭代…

YOLOv5-common.py文件

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 目录一、任务说明二、导入相关包和配置1.基本组件1.1 autopad1.2 Conv1.3 Focus1.4 Bottleneck1.5 BottleneckCSP1.6 C31.7 SPP1.8 Concat1.…

C3P0数据库连接池详解 及连接JDBC步骤

目录 一、基本定义 二、使用C3P0&#xff08;数据库连接池&#xff09;的必要性 1.JDBC传统模式开发存在的主要问题 三、数据库连接池的详细说明 四、使用连接池的明显优势 1.资源的高效利用 2.更快的系统反应速度 3.减少了资源独占的风险 4.统一的连接管理&#xff0c…

SQL优化实战-0001:SQL查找是否存在,不要再使用count

文章目录1.需求分析与普遍SQL写法2.问题分析3.优化方案4.总结5.补充5.1 还有什么时候可以使用LIMIT 15.2 什么时候没必要使用LIMIT 11.需求分析与普遍SQL写法 业务代码中&#xff0c;需要根据一个或多个条件&#xff0c;查询是否存在记录而不关心有多少条记录。普遍的SQL及代码…