首页 > 其他分享 >简单博弈论

简单博弈论

时间:2023-07-15 21:22:45浏览次数:31  
标签:dots prime 石子 xor space int 博弈论 简单

简单博弈论

Nim游戏

Nim游戏满足以下三个条件:
(1)两名玩家交替行动
(2)游戏过程中,可以执行的的行动和轮到哪位玩家没有关系
(3)不能行动的玩家判负
比如围棋就不是一种Nim游戏,因为围棋有黑白两子不满足(2),围棋判断输赢规则较为复杂不符合(3)。下面的取石子游戏就是一个Nim游戏:给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
例如:有两堆石子,第一堆有2个,第二堆有3个,先手必胜。
操作步骤:

  1. 先手从第二堆拿走1个,此时第一堆和第二堆数目相同
  2. 无论后手怎么拿,先手都在另外一堆石子中取走相同数量的石子即可。
  • 两个概念
    必胜状态:即经过某一个操作后留给另一个玩家的是一个必败状态,则对于先手玩家来说就是一个必胜状态,即先手可以走到一个必败状态。
    必败状态:即无论怎么操作都必定会走到一个必胜状态,则对于先手玩家来说是一个必败状态,即先手必定走到一个必胜状态。
  • 解决方法和数学证明
    假设这n堆石子分别为\(a_1,a_2,a_3\dots a_n\)个,那么当\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=0\)时为先手必败,\(a_1,a_2,a_3\dots a_n\)个,那么当\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n\ne0\)时为先手必胜。(xor是异或运算)
    证明:首先当每堆石子数量都为0时有:\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=0\)此时为终止状态,先手必败。当\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=x\ne0\)时,我们设x的二进制数为1的最高位是第k位,显然有\(a_1\)~\(a_n\)中至少有一个数的二进制数的第k位为1,不妨设这个数为\(a_i\),则\(a_i\space xor\space x<a_i\)[^异或计算],那么就可以保证从第i堆石子中取\(a_i-(a_i\space xor\space x)\)有意义,设取了以后第i堆剩下的石子个数为\(a^{\prime}=a_i-(a_i-(a_i\space xor\space x))=a_i\space xor\space x\),则取后各堆石子异或为:\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a^{\prime}\dots\space xor\space a_n=a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_i\space xor\space x\dots\space xor\space a_n=x\space xor\space x=0\)所以对于任一个各石堆个数异或值不为0的状态,我们都可以取特定的石子让它变为异或值为0的状态,即此时状态为先手必胜。再证:首先当\(a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=0\)此时为先手必败状态。这里我们可以用反证法,假设我们可以通过取一定的石子让它的异或值继续为0,即:
    不知道为什么,这下面的发不出来。

\[\begin{cases} a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=0&(1)\\ a_1\space xor\space a_2\space xor\space a_3\dots xor\space a^{\prime}\dots\space xor\space a_n=0&(2) \end{cases} $$(1),(2)两边同时异或得$a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n\space xor\space a_1\space xor\space a_2\space xor\space a_3\dots xor\space a^{\prime}\dots\space xor\space a_n=0$这里除了$a_i$和$a^{\prime}$外其余都是成对得所以$a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n\space xor\space a_1\space xor\space a_2\space xor\space a_3\dots xor\space a^{\prime}\dots\space xor\space a_n=0\Longleftrightarrow a_i\space xor\space a^{\prime}=0\Longrightarrow a_i=a^{\prime}$,Nim游戏中每一次都必须进行操作所以$a_i\ne a^{\prime}$,与规则矛盾,所以当$a_1\space xor\space a_2\space xor\space a_3\dots xor\space a_n=0$此时为先手必败状态,证毕。 [^异或计算]:异或计算是不进位加法,例如$1\space xor\space3=001\space xor\space 011=010=2$,这里$a_i=00\underbrace{1\dots}_{k}$ 而$x=00\underbrace{1\dots}_{k}$二者异或第k位必定变为0则必定小于$a_i$ - 代码实现 ```c++ #include<iostream> using namespace std; int main() { int n; cin>>n; int res=0; for(int i=0;i<n;i++) { int x; scanf("%d",&x); res^=x; } if(res==0) { printf("No"); } else { printf("Yes"); } return 0; } ``` ## sg函数 - mex操作 mex操作是指求一个集合中未出现的最小自然数,例如mex(1,2,3)=0,mex(0,1,2,4,5)=3。 - sg函数定义 在一个有向图中,对于每个节点x,从x出发有k条边,分别能到$y_1,y_2,y_3\dots y_k$则$sg(x)=mex(sg(y_1),sg(y_2),sg(y_3)\dots sg(y_k))$,特别地,终点的sg值为0。一个图的sg值定义为起点的sg值 ![输入图片说明](/imgs/2023-07-12/gQTU7PPHq7u2MWcC.png) 容易看出sg函数有性质: (1):若sg(x)=k$\ne 0$,则x点能到达的点的sg值最大为k-1. (2):sg值为0的点走不到sg值为0的点 (3):sg值不为0的点一定能走到sg值为0的点 - 定理 在一个图中,若sg(s)=0,则先手必败,若sg(s)$\ne 0$,则先手必胜。 在n个图中,若$sg(s_1)\space xor\space sg(s_2)\space xor\space sg(s_3)\dots\space xor\space sg(s_n)=0$为先手必败,反之必胜,从sg函数的性质很容易能够理解。 - 代码实现 给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S。 现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。 问如果两人都采用最优策略,先手是否必胜。 ```c++ #include<iostream> #include<unordered_set> #include<cstring> using namespace std; const int N=1e5+10,M=110; int f[N],g[M],h[M]; int k,n; int sg(int x) { if(f[x]!=-1) { return f[x];//让每个值的sg值只算一遍 } unordered_set<int> s;//用哈希表来存储x点取后会变为哪些状态 for(int i=0;i<k;i++) { int sum=g[i]; if(x>=sum) { s.insert(sg(x-sum)); } } for(int i=0;;i++) { if(!s.count(i))//注意哈希表如果什么也没有插入,查询0也会返回不存在,for循环从0开始所以这里可以保证终点的sg值一定为0 { f[x]=i; return f[x]; } } } int main() { cin>>k; memset(f,-1,sizeof(f)); for(int i=0;i<k;i++) { scanf("%d",&g[i]); } cin>>n; for(int i=0;i<n;i++) { scanf("%d",&h[i]); } int res=0; for(int i=0;i<n;i++) { res^=sg(h[i]); } if(res==0) { printf("No"); } else { printf("Yes"); return 0; } } ```\]

标签:dots,prime,石子,xor,space,int,博弈论,简单
From: https://www.cnblogs.com/Taco-gu/p/17556971.html

相关文章

  • 简单说明DNS、DHCP的主要作用是什么?
    1、DNS(DomainNameSystem,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析);每个IP地址都可以有一个主机名,主机名由......
  • Thread 和 ThreadPool 简单梳理(C#)【并发编程系列】
    〇、前言对于Thread和ThreadPool已经是元老级别的类了。Thread是C#语言对线程对象的封装,它从.NET1.0版本就有了,然后ThreadPool是.NetFramework2.0版本中出现的,都是相当成熟的存在。当然,现在已经出现了Task和PLinq等更高效率的并发类,线程和线程池在实际开发......
  • 关于x79itx主板的简单测试
    主板某宝的精粤x79itxoem主板,原生x79芯片。问了客服说有一个stat3.0和一个前置usb3.0,好像原生x79不支持usb3.0吧。主板图片:测试配件本次测试并未修改任何bios选项!硬盘:致钛PC005512g、致钛SC001256g内存:三星recc186616g*2CPU:e52660v2显卡:gt6101g电源:惠普拆机320......
  • 嗯,倒数日,开发了一个多月的倒数日 桌面应用 上线啦,简单暴力的显示方式,专注于显眼
    风向倒数日桌面应用是我个人历时一个月多开发的一个简单明了的倒数日应用,因为平时事情比较多,很多事情忙不过来,中间忽略了很多东西,甚至忘记时间该做什么,所以做了这个倒数日,为了就是提醒自己。刚刚开始才开始第一版本v1.0.0,其中还有很多小问题需要优化,但是还能将就用......
  • WPF TreeView 检测SelectedItem变化的简单方案
    TreeView无法绑定SelectedItem,而又想知道treeview的selecteditem的变化,当然目前有很多方法,我这里简单的提供一个。目前主要思路就是通过处理xaml的TreeViewItem的IsSelected属性来进行绑定。<TreeViewBorderThickness="0"Width="220"......
  • postgresql 简单使用
    编译安装的启动数据库:/usr/local/postgresql/bin/pg_ctl-D/data/postgresql-llogfilestart停止数据库:/usr/local/postgresql/bin/pg_ctl-D/data/postgresqlstop-mfast登录数据库:/usr/local/postgresql/bin/psqlpostgres 配置文件:/data/postgresql/postgresql.con......
  • 简单多项式
    前置知识复数\(i^2=-1\)复数为\(z=a+bi\)\(a+bi,a-bi\)互为共轭复数一个复数对应复平面上的任意一个点,一个复数与复平面上\((a,b)\)一一对应。复数与向量之间的运算有相似的地方,假设复数到复平面原点的距离为\(r\),那么复数也可以用\((r\cos\alpha,r\sin\alp......
  • 保持简单(Keep it simple)—-纪念丹尼斯•里奇(Dennis Ritchie)
    1954年,电气工程师阿利斯泰尔•里奇(AlistairE.Ritchie),决定举家从纽约州的布朗克斯维尔(Bronxville),搬到几十公里以外的新泽西。这样可以离他的工作单位”贝尔实验室”更近一些。13岁的丹尼斯•里奇(DennisRitchie),就这样随着父亲一起来到新泽西。那时,谁也没有......
  • Power APP Canvas组件简单控制画布控件
    效果图:图中绿色部分是组件,通过组件控制画布中按钮的点击事件。具体实现:1、组件按钮中赋值一个变量比如左按钮给yyy赋值false右按钮赋值true;2、增加输出属性                          将其赋值为此变量,此处用布尔......
  • RedisTemplate 的简单使用
    redisTemplate.opsForValue() 方法可以获得一个RedisString的操作类,通过该类可以执行一系列字符串类型数据的操作,例如获取、设置、删除数据等。//示例1:设置字符串类型的数据redisTemplate.opsForValue().set("key","value");//示例2:获取字符串类型的数据String......