博弈论(Nim 游戏)

公平组合游戏ICG

若—个游戏满足:

  1. 由两名玩家交替行动;
  2. 在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
  3. 不能行动的玩家判负;
    则称该游戏为一个公平组合游戏。
    NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件 2 2 2 和条件 3 3 3

可以看出,公平组合游戏不存在平局,而且一定可以结束。

Nim游戏

问题:给定  n n n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。

结论:如果所有石子数的异或和不等于 0 0 0,则先手必胜,反之先手必败。

正确性证明:
设第 i i i 堆石子的个数为 a i a_i ai 个, ⊕ \oplus 为异或符号

  • 如果每堆石子数都为 0 0 0,那么一定是先手必败,此时异或和为 0 0 0
  • 如果异或和 x x x 不是 0 0 0。设 x x x 在二进制下最高位 1 1 1 k k k,那么一定有一个 a i a_i ai 的二进制下第 k k k 位为 1 1 1,那么一定有 0 ≤ a i ⊕ x < a i 0 \le a_i \oplus x < a_i 0aix<ai,那么我们可以从第 a i a_i ai 堆拿走 a i − a i ⊕ x a_i - a_i \oplus x aiaix 个石子,那么 a i a_i ai 就变成了 a i − ( a i − a i ⊕ x ) = a i ⊕ x a_i - (a_i - a_i \oplus x) = a_i \oplus x ai(aiaix)=aix,对于整体的异或和,就相当于又异或了一个 x x x x ⊕ x = 0 x \oplus x = 0 xx=0,所以我们一定可以一步吧当前 x ≠ 0 x \not= 0 x=0 变为 x = 0 x = 0 x=0,使得对手每次面对的都是 x = 0 x = 0 x=0
  • 如果 x x x 0 0 0。我们拿完石子后一定不能让 x x x 依旧为 0 0 0。反证法:设我们拿完石子后可以让 x x x 0 0 0,那么我们从第 a i a_i ai 堆拿 k k k 颗石子,那么新异或和 y y y 就等于 x ⊕ a i ⊕ ( a i − k ) = 0 x \oplus a_i \oplus (a_i - k) = 0 xai(aik)=0,因为 x = 0 x = 0 x=0,所以 y = a i ⊕ ( a i − k ) = 0 y = a_i \oplus (a_i - k) = 0 y=ai(aik)=0,所以 a i = a i − k a_i = a_i - k ai=aik,所以 k = 0 k = 0 k=0。而 k > 0 k > 0 k>0,所以假设失败,也就是证明了我们拿完石子后不可能让 x x x 依旧为 0 0 0

总结一下,主要有三条关系:

  • 如果 a i a_i ai 都等于 0 0 0,那么一定先手必败。
  • 我们一定可以一步吧当前 x ≠ 0 x \not= 0 x=0 变为 x = 0 x = 0 x=0
  • 我们拿完石子后不可能让 x x x 依旧为 0 0 0
    也就是说我们一定可以让,对手面对的每次都是 x = 0 x = 0 x=0。但他无法改变,到我时 x ≠ 0 x \not = 0 x=0。又因为石子总数是不断减少的,所以他一定会先面临到 a i a_i ai 都等于 0 0 0 的状态,此时无法移动,也就盘他为失败,对于我就是胜利。说明结论成立。

在这里就可以看出,先手必胜态为 x ≠ 0 x \not = 0 x=0;先手必败态为 x = 0 x = 0 x=0

值得注意的是,这个结论表述不是唯一的,但是和别的结论表述是等价的。

例题

891. Nim游戏 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;int n, m;int main()
{cin >> n;while (n -- ){int a;scanf("%d", &a);m ^= a;}if (m == 0) cout << "No";else cout << "Yes";return 0;
}

变化不大
892. 台阶-Nim游戏 - AcWing题库

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;int n, m;int main()
{cin >> n;int res = 0;for (int i = 1; i <= n; i ++ ){int a;scanf("%d", &a);if (i & 1) res ^= a;}if (res) puts("Yes");else puts("No");return 0;}

P1247 取火柴游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

绿色的水题,看懂上面的 Nim 游戏就能做出来。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>using namespace std;const int N = 500010;int n, m;
int g[N];int main()
{cin >> n;int res = 0;for (int i = 1; i <= n; i ++ ) {scanf("%d", &g[i]);res ^= g[i];}if (res){int k = 0;for (int i = 31; i >= 0; i -- ){if (res >> i & 1) {k = i;break;}}for (int i = 1; i <= n; i ++ ){if (g[i] >> k & 1) {cout << g[i] - (g[i] ^ res) << ' ' << i << endl;g[i] = g[i] ^ res;break;}}for (int i = 1; i <= n; i ++ ) cout << g[i] << ' ';}else puts("lose");return 0;
}

P1288 取数游戏 II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这题就是简单的不知道怎么做,和博弈有关系,但完全没必要。越想多越复杂

反 Nim 游戏

玩法和 Nim 游戏一样,只不过胜利标准变成了,不可移动为胜利。

结论:先手必胜条件为(下面为先手必胜的两种方式):

  1. 所有 a i = 1 a_i = 1 ai=1,且 a i a_i ai 的数量为偶数,则先手必胜。
  2. 当至少有一个 a i > 1 a_i > 1 ai>1 ,且所有 a i a_i ai 的异或和 x x x 为不等于 0 0 0,即 x ≠ 0 x \not= 0 x=0

证明:
对于结论 1 1 1,显而易见,略过。
对于结论 2 2 2
(1)当 a i > 1 a_i > 1 ai>1 只有 1 1 1 个时。因为先手可以操控这堆为 1 1 1 还是为 0 0 0,即可以操控 a i = 1 a_i = 1 ai=1 的数量为奇数还是偶数,因此这是必胜态,且此时 x ≠ 0 x \not= 0 x=0(易得)。
(2)当 a i > 1 a_i > 1 ai>1 至少有两个时。这也有两种状态。

  1. x = 0 x = 0 x=0 时。此时根据 Nim 游戏可知,操作一次后 x x x 必定不等于 0 0 0,即变成下面的 2. ;或者操作一次后 a i > 0 a_i > 0 ai>0 仅剩一个,即回 ( 1 ) (1) (1),即给对方为必胜态。
  2. x ≠ 0 x \not= 0 x=0 时,根据 Nim 游戏可知,此时一定可以 x = 0 x = 0 x=0,且 a i > 1 a_i > 1 ai>1 个数一定大于 2 2 2(如果只有一个 a i > 1 a_i > 1 ai>1,那么就和 ( 1 ) (1) (1) x ≠ 0 x \not= 0 x=0 相悖)。即回到 1.。

可以看出 ( 2 ) . 1 (2).1 (2).1 一定是必败态(因为它只能给对方必胜态,或者 ( 2 ) . 2 (2).2 (2).2,而 ( 2 ) . 2 (2).2 (2).2 一定可以回到 ( 2 ) . 1 (2).1 (2).1,即不断循环,直到给对方必胜态),而 ( 2 ) . 2 (2).2 (2).2 就是必胜态。

( 2 ) (2) (2) ( 1 ) (1) (1) 结合起来就得到,结论 2 2 2。证毕

SG函数

基本定义

Mex运算
S S S 表示一个非负整数集合。定义 mex ⁡ ( S ) \operatorname {mex}(S) mex(S) 为求出不属于集合 S S S 的最小非负整数的运算,即:
mex ⁡ ( S ) = min ⁡ x \operatorname {mex}(S) = \min{x} mex(S)=minx x x x属于自然数,且 x x x 不属于 S S S

有向图游戏
给定一个有向无环图,图中有一个唯一的起点,在起点上放有一枚棋子。两名玩家交替地把这枚棋子沿有向边进行移动,每次可以移动一步,无法移动者判负。该游戏被称为有向图游戏。任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连有向边。

SG函数
在有向图游戏中,对于每个节点 x x x,设从 x x x 出发共有 k k k 条有向边,分别到达节点 y 1 , y 2 … y k y_1,y_2 \dots y_k y1,y2yk,定义 SG ⁡ ( x ) \operatorname {SG}(x) SG(x) k k k 的后继节点 y 1 , y 2 … y k y_1,y_2 \dots y_k y1,y2yk 的SG函数值构成的集合再执行 mex ⁡ ( S ) \operatorname {mex}(S) mex(S) 运算的结果,即:
SG ⁡ ( x ) = mex ⁡ [ SG ⁡ ( y 1 ) , SG ⁡ ( y 2 ) … SG ⁡ ( y k ) ] \operatorname {SG}(x) = \operatorname {mex}[\operatorname {SG}(y_1), \operatorname {SG}(y_2) \dots \operatorname {SG}(y_k)] SG(x)=mex[SG(y1),SG(y2)SG(yk)]
特别地,整个有向图游戏 G 的 SG 函数值被定义为有向图游戏起点 s s s 的 SG 函数值,即 SG ⁡ ( G ) = SG ⁡ ( s ) \operatorname {SG}(G) = \operatorname {SG}(s) SG(G)=SG(s)

使用方法

一般的博弈论问题都可以转化为 SG 函数求解。

以上为基本定义,现在来说说是干什么的。

一般 SG 函数形式为: SG ⁡ ( 状态 ) \operatorname {SG}(状态) SG(状态)
首先终止状态的 SG 值为 0,即: SG ⁡ ( 终点状态 ) = 0 \operatorname {SG}(终点状态) = 0 SG(终点状态)=0。按照 SG 函数的运算,可得如图(红色为当前节点的 SG 值):
![[博弈论1.png|269]]
如图可知,每个 SG 值不为 0 0 0 的点,一定可以到一个 SG 值为 0 0 0 的点;同理每个 SG 值为 0 0 0 的点只能到一个 SG 值不为 0 0 0 的点或者无法移动。这就和上面的 Nim 游戏有点像,如果先手(SG 值)不为 0,那么我可以让后手的每一次都为 0,反之同理。可以知道,SG 函数不为 0 那么先手必胜,反之先手必败。

因此对于两个绝顶聪明的人,这类游戏在开始就决定了胜负。

上面是一个图的情况。如果这个图有很多个会怎么样?即一个游戏,既可以在一个图上移动,也可以在其他图上移动。那么一个状态,有很多 SG 值,这时候就把每个图起始的 SG 函数都异或起来,变成一个异或和 x x x 即可,如果 x = 0 x = 0 x=0,则先手必败,反之先手必胜。这个证明和 Nim 游戏的证明思路是一模一样的,这里不赘述。

注意有的题目需要计算 SG 函数,但是有的题可以不用,如最上面两个 Nim 游戏,直接运用结论就很快,当然也可以算 SG 函数,Nim游戏比较特殊,每堆石子数恰好就是它的 SG 函数值。

例题

893. 集合-Nim游戏 - AcWing题库

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>using namespace std;const int N = 10010;int n, m;
int g[N], f[N];
int sum;int sg(int x)
{if (f[x] != -1) return f[x];unordered_set<int> s;for (int i = 1; i <= n; i ++ )if (x - g[i] >= 0) s.insert(sg(x - g[i]));for (int i = 0; ; i ++ )if (!s.count(i)) return f[x] = i; 
}int main()
{cin >> n;for (int i = 1; i <= n; i ++ ) cin >> g[i];cin >> m;memset(f, -1, sizeof f);while (m -- ){int s;cin >> s;sum ^= sg(s);}if (sum) puts("Yes");else puts("No");return 0;
}

894. 拆分-Nim游戏 - AcWing题库
这个也简单,但是涉及了 SG 函数的本质。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>using namespace std;const int N = 110;int n, m;
int f[N];int sg(int x)
{if (f[x] != -1) return f[x];unordered_set<int> s;for (int i = 0; i < x; i ++ ){int a = sg(i);for (int j = 0; j <= i; j ++ ){int b = sg(j);s.insert(a ^ b);}}for (int i = 0; ; i ++ ){if (s.count(i) == 0) return f[x] = i;}
}int main()
{cin >> n;memset(f, -1, sizeof f);int res = 0;for (int i = 1; i <= n; i ++ ){int a;cin >> a;res ^= sg(a);}if (res) puts("Yes");else puts("No");return 0;
}

拓展

博弈论的核心:保持自己在可胜态,且让对手无法改变其可败态或者改变我的可胜态。

可胜态,不是必胜态,但只要一直保持自己是可胜态,最后就必胜。同理,可败态不是必败态,但只要能让他一种在可败态,那么就是必败。

阻止对方所有能让我变成非可胜态的举动。(必胜态主导必败态,也就是说除非必胜放水,必败态无法转移到必胜态)。

另一种解释:如果一个状态可以到达一个必败态,那么它就是必胜态;如果一个状态不能到达一个必败态,他就是必败态。

例题

以下的题较难。
1321. 取石子 - AcWing题库

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

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

相关文章

Java17 --- SpringSecurity之前后端分离处理

目录 一、实现前后端分离 1.1、导入pom依赖 1.2、认证成功处理 1.3、认证失败处理 1.4、用户注销处理 1.5、请求未认证处理 1.6、跨域处理 1.7、用户认证信息处理 1.8、会话并发处理 一、实现前后端分离 1.1、导入pom依赖 <dependency><groupId>co…

Zabbix 7.0 LTS新特征

Zabbix 7.0 LTS版本是基于GNU Affero通用公共许可证第3版&#xff08;AGPLv3&#xff09;发布的&#xff0c;7.0 LTS更新了许多新的功能&#xff0c;包含合成终端用户Web监控、Zabbix proxy高可用性和负载均衡、重大性能和可扩展性提升、原生多因子认证&#xff08;MFA&#xf…

【python】python股票量化交易策略分析可视化(源码+数据集+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

计组_计算机概要与设计

2024.06.23~2024.06.27&#xff1a;计算机组成原理学习笔记 CH1 计算机概要与设计 1.1 计算机的分类1.1.1 传统按照应用分类1.1.2 后PC时代1.1.3 存储容量 1.2 八大伟大设计思想1.2.1 两个设计原则1.2.2 四个提高性能1.2.3 存储器层次1.2.4 冗余提高可靠性 1.3 软硬件基础1.3.1…

React之useEffect

在React中&#xff0c;useEffect 是一个非常重要的Hook&#xff0c;它用于管理副作用操作。副作用指的是那些不直接与组件渲染相关的操作&#xff0c;例如数据获取、订阅、手动DOM操作等。本文将详细介绍 useEffect 的概念、基础使用、参数说明以及如何清除副作用&#xff0c;并…

软复位和硬复位

“硬复位”和“软复位”&#xff1a; 硬复位&#xff08;hard reset&#xff09;&#xff1a;通过外部复位引脚或者电源重启来实现的复位方式。 当硬复位信号有效时&#xff0c;系统会停止所有操作&#xff0c;并将所有寄存器和状态重置为初始状态。硬复位通常由硬件按钮或电…

【fastapi+mongodb】使用motor操作mongodb(三)

本篇文章介绍mongodb的删和改&#xff0c;下面是前两篇文章的链接&#xff1a; 【fastapimongodb】使用motor操作mongodb 【fastapimongodb】使用motor操作mongodb&#xff08;二&#xff09; delete delete 的用法基本和查找一致&#xff0c;包括delete_one&#xff08;删除…

借助AI快速提高英语听力:如何获得适合自己的听力材料?

英语听力是英语学习中的一个重要组成部分&#xff0c;它对于提高语言理解和交流能力至关重要。可理解性学习&#xff08;comprehensible input&#xff09;是语言习得理论中的一个概念&#xff0c;由语言学家Stephen Krashen提出&#xff0c;指的是学习者在理解语言输入的同时&…

如何使用STL中的模板类

在C中&#xff0c;标准模板库&#xff08;STL&#xff09;提供了大量的模板类&#xff0c;这些类可以处理各种类型的数据&#xff0c;从而极大地提高了代码的复用性和灵活性。要使用STL中的模板类&#xff0c;你需要遵循一些基本的步骤和约定。 以下是一些使用STL模板类的基本…

时空预测 | 基于深度学习的碳排放时空预测模型

时空预测 模型描述 数据收集和准备&#xff1a;收集与碳排放相关的数据&#xff0c;包括历史碳排放数据、气象数据、人口密度数据等。确保数据的质量和完整性&#xff0c;并进行必要的数据清洗和预处理。 特征工程&#xff1a;根据问题的需求和领域知识&#xff0c;对数据进行…

Canvas绘制图片和区域

如何使用Canvas在图片上绘制区域&#xff1f; 一. 首先&#xff0c;我们需要初始化三个canvas画布&#xff08;初始化Canvas&#xff09; initCanvas() {// 初始化canvas画布let canvasWrap document.getElementsByClassName("canvas-wrap");this.wrapWidth canva…

Android中RSA公钥加密后Java服务端私钥无法解密问题解决

工作中经常需要Android客户端使用RSA公钥加密敏感数据&#xff0c;服务端再使用配套的RSA私钥解密数据&#xff0c;最近碰到一个问题&#xff0c;使用RSA加密后服务端无法解密&#xff0c;查阅相关资料后&#xff0c;发现是这个问题&#xff1a; RSA操作的填充方式不对。 and…

Android中如何动态的调整Dialog的背景深暗

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 在 Android 开发中&#xff0c;当你使用 Dialog 或 DialogFragment 时&#xff0c;可以通过设置 Window 的背景变暗来突出它的可见性。这个效果…

【密码学】分组密码

文章目录 分组密码的模式分组密码与流密码模式明文分组与密文分组 ECB模式ECB定义ECB特点对ECB模式的攻击改变分组顺序攻击 CBC模式CBC定义初始化向量IVCBC特点对CBC模式的攻击对初始向量进行反转攻击填充提示攻击 CFB模式CFB定义对CFB模式的攻击重放攻击 OFB模式OFB定义CFB模式…

05-5.5.3 并查集的进一步优化

&#x1f44b; Hi, I’m Beast Cheng &#x1f440; I’m interested in photography, hiking, landscape… &#x1f331; I’m currently learning python, javascript, kotlin… &#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…

游戏心理学Day23

游戏中的道德与文化 游戏与道德 道德在汉语中最早可追溯到老子的道德经&#xff0c;老子说道生之&#xff0c;德畜之&#xff0c;物行之&#xff0c;势成之&#xff0c;是以万物莫不遵循而贵德。道之贵&#xff0c;德之贵&#xff0c;夫莫之命&#xff0c;而常于自然。其中&a…

全面分析一下前端框架Angular的来龙去脉,分析angular的技术要点和难点,以及详细的语法和使用规则,底层原理-小白进阶之路

Angular 前端框架全面分析 Angular 是一个由 Google 维护的开源前端框架。它最早在 2010 年发布&#xff0c;最初版本称为 AngularJS。2016 年&#xff0c;团队发布了一个完全重写的版本&#xff0c;称为 Angular 2&#xff0c;之后的版本&#xff08;如 Angular 4、Angular 5…

什么是CSS原子化?

CSS原子化&#xff0c;也被称为功能性CSS或工具类CSS&#xff0c;是一种构建样式表的方法&#xff0c;它将传统CSS中的“多属性-多值”类转变为“单属性-单值”的类。这种方法最主要的特点是提高了样式的可复用性和模块化程度。 CSS原子化的详细说明&#xff1a; 结构和命名 …

【LocalAI】(13):LocalAI最新版本支持Stable diffusion 3,20亿参数图像更加细腻了,可以继续研究下

最新版本v2.17.1 https://github.com/mudler/LocalAI/releases Stable diffusion 3 You can use Stable diffusion 3 by installing the model in the gallery (stable-diffusion-3-medium) or by placing this YAML file in the model folder: Stable Diffusion 3 Medium 正…

PriorityQueue详解(含动画演示)

目录 PriorityQueue详解1、PriorityQueue简介2、PriorityQueue继承体系3、PriorityQueue数据结构PriorityQueue类属性注释完全二叉树、大顶堆、小顶堆的概念☆PriorityQueue是如何利用数组存储小顶堆的&#xff1f;☆利用数组存储完全二叉树的好处&#xff1f; 4、PriorityQueu…