Acwing 简单博弈论

news2024/11/23 7:40:27

公平组合游戏ICG
若一个有限满足:由两名玩家交替行动:

  • 在游戏进程的任意时刻;
  • 可以执行的合法行动与轮到哪名玩家无关;
  • 不能行动的玩家判负;

则称该游戏为一个公平组合游戏。NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋就不是公平组合游戏。因为围棋交战双方只能落黑子和白子,胜负判定也比较复杂,不满足条件2和3.

Nim游戏
给定N堆物品,第i堆物品由Ai个。两名玩家轮流行动,每次可以任选一堆,取走多个物品,可把一堆取光,但不能不取,取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。

我们把这种游戏称为NIM博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。 所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。 NIM博弈不存在平局,只有先手必胜和先手必败两种情况。

  • 必胜状态:先手进行某一个操作,留给后手是一个必败状态时,对于先手来说是一个必胜状态。即先手可以走到某一个必败状态
  • 必败状态:先手无论如何操作,留给后手都是一个必胜状态时,对于先手来说是一个必败状态。即先手走不到任何一个必败状态。
    结论:假设n堆物品,数目分别为a1,a2,a3…an,如果a1⊕a2⊕a3.......⊕an != 0,则先手必胜;否则为0,先手必败

证明过程我们不必理会,记住这个结果,代码还是很简单的。
Acwing 891.Nim游戏
在这里插入图片描述

具体实现代码(详解版):

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int n;
    cin >> n; // 读取石子堆的数量 n
    int res = 0; // 用来存储所有石子堆数量的异或结果
    while (n--) { // 读取每一堆的石子数量
        int a;
        cin >> a; // 读取每堆石子的数量
        res ^= a; // 将每个堆的石子数与当前异或结果进行异或
    }
    
    // 判断异或结果是否为0
    if (res) puts("Yes"); // 如果 res 不为 0,表示先手必胜
    else puts("No");      // 如果 res 为 0,表示先手必败
    
    return 0;
}

下面看一个变形题目:

Acwing 892 台阶-Nim游戏
在这里插入图片描述
实现思路:此时我们需要将奇数台阶看做一个经典的Nim游戏,如果先手时奇数台阶上的值的异或值为0,则先手必败,反之必胜

具体实现代码(详解版):

#include <iostream>
#include <algorithm>

using namespace std;

int main(){
    int n ,res = 0;
    cin >> n ;
    for(int i = 1 ; i <= n ; i ++){
        int a; 
        cin >> a;
        if(i % 2) res ^= a;//选择奇数台阶进行异或
    }
    if(res) puts("Yes");//异或非零必胜
    else puts("No");
    
    return 0;
}

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

Mex运算
设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即mex(S)=min{x},x属于自然数,且x不属于S。eg:mex({1,2,3})=0,mex({0,1,2})=3;

SG函数
在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1, y2, …, yk,定义SG(x)为x的后继节点y1, y2, …, yk 的SG函数值构成的集合再执行mex(S)运算的结果,即: SG(x) = mex({SG(y1), SG(y2), …, SG(yk)}) 特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(起点)。且图的终点(即不含出边的点)的SG函数值为0.

定理:

  • 有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值不为0。
  • 有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。

有向图游戏的和
设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:SG(G)=SG(G1)⊕SG(G2)⊕··⊕ SG(Gm)

Acwing 893.集合-Nim游戏
在这里插入图片描述
若有一堆的数量为10,取法为2,5,则他的状态图如下所示,就是每次选择取法不同,出现不一样的分支
在这里插入图片描述
得到其SG函数的值,即SG(10)=1。同理计算其他石子堆的的SG值,将所有堆SG值异或,若不为0,则先手必胜,若为0则先手必败

实现思路:

  • 设置取法数组s[],所有石子堆共享操作
  • 设置一个数组f,f[x]记录图中某点x的SG值,初始为-1表示x还未计算。注意只要x的SG确定了那么就不会再改变,因为针对同一个数x,无论在哪个石子堆,他就只能执行相同的操作,因为所有石子堆共享取法集合。比如x=5,取法{2,5},那么在任何石子堆,x的出边只能为0或3,固定的;
  • 这里使用一个哈希表S存储当前点出边点的SG值,以此确定当前点的SG值,可实现自动排序,和判断某个值是否出以此来完成mex操作(选出最小且没有出现的自然数)。哈希表S必须定义为局部变量,每次递归都会使用新的哈希表,所以一个图其他不与当前点相连的点的存在不会影响当前点SG的计算

为什么哈希S不能开全局,只能作为局部变量?
对于集合-Nim,注意到0这个值可以被映射多次(如上图),这意味着有多个可能的值xi作为叶子节点(末尾节点),满足f[xi] = 0。而如果将S作为全局变量,则其中只能有一个值x’映射到0,由mex函数知其他的值都将映射到大于0的值(即f[x’] = 0,f[xi/x’] > 0,xi/x’代表集合x排除掉x’所剩值组成的集合),因而不能将S作为全局变量,防止不同点的相互影响。

具体实现代码(详解版):

#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_set>

using namespace std;

const int N = 110, M = 10010; 

int n, m; 
int s[N], f[M]; // s 存储取法数,f 存储 SG 值

// 计算状态 x 的 SG 值
int sg(int x) {
    if (f[x] != -1) return f[x]; // 如果 SG 值已计算过,直接返回

    unordered_set<int> S; // 哈希表 存储x能到的点的SG值,以此确定x的值,每次递归都是最新的
    for (int i = 0; i < m; i++) { // 遍历所有操作数
        int sum = s[i];
        if (x >= sum) S.insert(sg(x - sum)); // 一条分支路径 直到终点
    }
    
    // mex操作 确定x的SG值
    for (int i = 0;; i++) {
        if (!S.count(i)) 
            return f[x] = i; // 选出最小的没有出现的自然数,赋给x的SG值
    }
}

int main() {
    cin >> m; 
    for (int i = 0; i < m; i++) cin >> s[i]; 
    cin >> n; 
    
    memset(f, -1, sizeof f); // 初始化 SG 值数组为 -1,表示还未计算
    
    int res = 0; 
    for (int i = 0; i < n; i++) {int x;
        cin >> x; 
        res ^= sg(x); //各个堆的SG值异或,每个堆的SG=起点的SG值
    }
    
    // 根据结果判断先手是否有必胜策略
    if (res) puts("Yes"); // 如果 res 不为 0,先手必胜
    else puts("No");      // 如果 res 为 0,先手必败
    
    return 0;
}

Acwing 894.拆分-Nim游戏
在这里插入图片描述
实现思路:首先可以看出必然会有解(有限的),因为每次放入的两堆规模各自都会比原规模小,各堆石子每次操作后呈递减趋势,最后必然会趋于0,比如一堆石子个数是1,取走以后,只会再放入两堆个数为0的石子。相比于集合-Nim,这里的每一堆可以变成小于原来那堆的任意大小的两堆
即a[i]可以拆分成(b[i], b[j]),为了避免重复规定b[i]>= b[j],即: a[i]>b[i]>= b[j]
相当于一个局面拆分成了两个局面,由SG函数理论,多个独立局面的SG值,等于这些局面SC值异或和。因此需要存储的状态就是sg(b[i])^sg(b[j])(与集合-Nim的唯一区别)
PS:因为这题中原堆拆分成的两个较小堆小于原堆即可,因此任意一个较小堆的拆分情况会被完全包含在较大堆中,因此S可以开全局。当然也可以在函数中定义。

具体实现代码(详解版):

#include <iostream>
#include <cstring>
#include <unordered_set>

using namespace std;
const int N = 110;

int n;
int f[N];

unordered_set<int> S;

//求sg的值
int sg(int x){
    if(f[x] != -1) return f[x];//表示已经求过了 无需再求
    
    //将一堆石子拆分成两堆更小的石子i ,j
    for(int i = 0 ;i < x ; i ++){
        for(int j = 0 ; j <= i ; j ++){//规定j不大于i,避免重复计算
            S.insert(sg(i) ^ sg(j));//由SG的函数理论,多个独立局面的SG值等于这些局面SG值的异或
        }
    }
    //mex操作 得到不存在的最小数
    for(int i = 0 ; ; i ++){
        if(!S.count(i))
          return f[x] = i;
    }
}

int main(){
    memset(f , -1 ,sizeof f);
    
    cin >> n;
    int res = 0;
    while(n --){
        int x;
        cin >> x;
        res ^= sg(x);
    }
    
    if(res) puts("Yes");
    else puts("No");
    
    return 0;
}

以上就是一些简单博弈论的题目,需要把数学公式给提炼出来。

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

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

相关文章

鸢尾花书实践和知识记录[编程1-11二维和三维可视化]

作者空间 文章目录 思维导图函数使用 二维可视化方案平面散点图散点图的示例代码1&#xff1a;绘制鸢尾花的散点图代码2Plotly绘制散点图 数据类型和绘图工具的对应 平面等高线代码3生成等高线网格数据 plotly.express关键的绘图函数 Plotly的另一个模块代码4 Plotly生成的 热图…

以旅游购物贸易方式报关出口的货物是什么意思

旅游购物贸易方式的定义 ‌旅游购物贸易‌是指国内外旅游者通过旅游渠道&#xff0c;携带外币现钞、外币票据入境&#xff0c;到外汇指定银行兑换成人民币&#xff0c;并在特定区域进行商品采购、报检、报关后&#xff0c;将所购货物运回国内进行销售的贸易行为。这种贸易方式通…

官方外卖霸王餐对接接口渠道如何选择?

对接官方外卖霸王餐接口渠道通常涉及以下步骤&#xff1a; 选择服务提供商&#xff1a;选择一个提供外卖霸王餐API接口服务的平台。注册与申请&#xff1a;在选定服务提供商的平台上进行注册并创建账号&#xff0c;然后提交API接口使用申请。获取接口文档和密钥&#xff1a;申…

CSS基础-常见属性

6、CSS三大特性 6.1 层叠性 如果样式发生冲突&#xff0c;则按照优先级进行覆盖。 6.2 继承性 元素自动继承其父元素、祖先元素所设置的某些元素&#xff0c;优先继承较近的元素。 6.3 优先级 6.3.1 简单分级 1、内联样式2、ID选择器3、类选择器/属性选择器4、标签名选择器/…

SpringBoot框架下的社区医院信息系统开发

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理社区医院信息平台的相关信息成为必然。开发…

钢琴灯品牌排行榜前十名有哪些?护眼灯钢琴灯品牌排行榜

钢琴灯品牌排行榜前十名有哪些&#xff1f;要说近期比较火爆的家电产品&#xff0c;那一定绕不开护眼灯钢琴灯。作为能够提高光线质量&#xff0c;帮助我们营造舒适环境的热门好物&#xff0c;其受到了很多专业人士的认可。但是作为一名专业的家电测评师&#xff0c;我想在此提…

找到字符串中第一个匹配项的下标(c语言)

1./给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 //示例 1&#xff1a; //输入&#xff1a;haystac…

plt用数组显示图像

目录 plt.imshow实战 plt.imshow plt.imshow 可以用来显示二维数组表示的图像&#xff0c;也可以用来显示热图&#xff08;heatmap&#xff09;等。基本语法如下&#xff1a; plt.imshow(X, cmapviridis, interpolationnearest)参数说明&#xff1a; X&#xff1a;二维数组&…

基础岛第5关:XTuner微调个人小助手认知

进入开发机 克隆Tutorial仓库 mkdir -p /root/InternLM/Tutorialgit clone -b camp3 https://github.com/InternLM/Tutorial /root/InternLM/Tutorial 创建虚拟环境 在安装 XTuner 之前&#xff0c;我们需要先创建一个虚拟环境。使用 Anaconda 创建一个名为 xtuner0121 的虚拟…

工作日志:nvm版本控制遇到的一系列问题。

1、安装vue3可使用的富文本编辑器。&#xff08;https://www.wangeditor.com/v5/for-frame.html#demo-1&#xff09; npm install wangeditor/editor-for-vuenext --save2、为同时拥有两个类的元素设置样式&#xff0c;组合选择器是通过在选择器中并列写入两个类名来实现的&am…

openpnp - 执行M400命令后,超时错误的解决方法

文章目录 openpnp - 执行M400命令后&#xff0c;超时错误的解决方法概述笔记备注END openpnp - 执行M400命令后&#xff0c;超时错误的解决方法 概述 在X轴齿隙校正时&#xff0c;出现M400的命令超时错误。能重现。 查了资料&#xff0c;有人遇到过。看了github上的一个帖子(…

STM32(四)LED闪烁、流水灯及蜂鸣器操作

小节任务&#xff1a;在对GPIO函数初始化操作及配置好输入或输出模式后&#xff0c;使用GPIO的输入输出函数控制LED闪烁、流水灯及蜂鸣器操作&#xff0c;本小节先使用GPIO的四个输出函数 SetBits函数将指定端口设置为高电平 ResetBits函数将指定端口设置为低电平 WriteBit根据…

Tiny-universe手戳大模型TinyRAG--task4

TinyRAG 这个模型是基于RAG的一个简化版本&#xff0c;我们称之为Tiny-RAG。Tiny-RAG是一个基于RAG的简化版本&#xff0c;它只包含了RAG的核心功能&#xff0c;即Retrieval和Generation。Tiny-RAG的目的是为了帮助大家更好的理解RAG模型的原理和实现。 1. RAG 介绍 LLM会产…

Linux中的进程间通信之共享内存

共享内存 共享内存示意图 共享内存数据结构 struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kerne…

OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离

OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离 —— 2024-10-02 下午 code review! 文章目录 OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离1.代码图片2.分析3.UML4.代码 1.代码图片 运行 Mouse button 1 pressed at (100, 200) Mouse dragged by (50, 50)…

老年人最真实的养老需求

子孝父心宽。&#xff08;陈元靓&#xff09; 向往的养老胜地 下文节选(有删改&#xff09;&#xff1a; 中消协发布报告 揭示老年人最真实的养老服务需求 主页&#xff1a; 博客 文章目录 前言1. 需求端1.1 居家养老更关注家政餐饮等“日常所需”1.2 机构养老的需求重在“医…

猫咪尿闭的症状有哪些?适合尿闭猫咪的猫罐头盘点

刚开始养猫的时候只喂猫粮&#xff0c;也没督促孩子喝水&#xff0c;也没喂猫罐头&#xff0c;结果猫咪尿闭了&#xff0c;每次上厕所都在惨叫&#xff0c;完全无法排尿。带去医院治疗&#xff0c;前前后后总共花了8000&#xff0c;真实花钱买教训。我总结了一下猫咪尿闭时会出…

kettle从入门到精通 第八十九课 ETL之kettle kettle jms activemq使用教程

场景&#xff1a;群里有小伙伴求助jms activemq如何使用kettle 进行消费数据&#xff0c;之前连接过kafka&#xff0c;rabbtimq&#xff0c;想着activemq应该也没啥难度&#xff0c;结果低估了activemq。盘他&#xff01;&#xff01;&#xff01; 插曲&#xff1a;ActiveMq 有…

SQL连接Python

对于运营部门的Yoyo来说&#xff0c;她想要知道夜曲优选的订单都来自哪些省份&#xff0c;每个省份的总订单数以及总订单金额分别是多少。 这时小鹿就会通过SQL对连接的数据库进行查询&#xff0c;再将结果传递给Python处理&#xff0c;并帮助Yoyo生成可视化图表。 我们先来快…

工程师 - Github文件访问加速

如果是下载单独的又比较大的Github上的文件&#xff0c;网速较差时&#xff0c;可以使用镜像网站&#xff0c;比如下面这个工具。 GitHub 文件加速 - Sonder (akams.cn) GitHub 文件加速 - Sonder 另外&#xff0c;也可以在搜索引擎里直接搜索“Github 文件加速”&#xff0c;…