首页 > 其他分享 >从ICG到SG函数

从ICG到SG函数

时间:2024-10-30 21:49:44浏览次数:5  
标签:函数 必胜 必败 ICG int SG 游戏

SG函数是用于解决博弈论中公平组合游戏(ICG)问题的一种方法

ICG

这是啥?

定义大概就几条:

  1. 双方参与,轮流决策,决策最优
  2. 无法决策时游戏结束,无法决策者输,不论如何决策游戏都能在有限步完成
  3. 同一状态不可多次抵达,游戏无平局,任意决策者在决策点的行为与决策者无关仅与决策点有关

这就是 ICG,公平组合游戏

典型例子是 nim 游戏

必胜必败态

必败态:记为 P,直观理解就是到达这个状态的人已经必死无疑了
必胜态:记为 N,直观理解就是到这里就稳了,脑子不犯蠢就一定会赢

可以证明 ICG 里任意状态一定都是这两种状态,有策梅洛定理:

策梅洛定理:ICG 游戏中,任何一个局面先手或者后手其中之一必然存在必胜策略

如何判断一个状态是 NP 呢?

最直观的方法是:

  • 无法移动的状态一定是 P,称为初必败态
  • 能够移动到必败态的一定是必胜态,因为你只要往必败态走就行了
  • 只能移动到必胜态的就是必败态,因为你无论如何行动对手都必胜了

DAG的博弈

有这样一个问题

给定一个有向无环图,在给定的起点处有一个棋子,两个炒鸡聪明的人交替移动这个棋子,不能移动者输

看起来是一个很呆的问题,但是这个问题单独拿出来的意义在于 任意一个 ICG 问题都可以归纳到这种形式(状态为点,状态间连边)

mex运算

马上要开始探究 SG 函数,但是在此之前......

首先我们需要定义一个奇怪的运算,mex

这是一个数集的运算符号,\(mex(S)\) 代表的是不在集合 \(S\) 中 的最小自然数

例如 \(mex(\{0,1,3,5\})=2\)

SG函数

用 \(v(u,v)\) 表示从 \(u\) 到 \(v\),存在直接的有向边,则

对于给定的 DAG,我们定义每个点的SG函数为:

\(SG(x)=mex(\{SG(y)|v(x,y)\})\)

但是只有这一个函数呢,他是啥用么有的

SG函数的性质

  • 所有汇点(也就是各个初必败态)的 \(SG\) 函数值为 \(0\)

这个应该比较显然的说,因为此时后继状态都是空集

  • \(SG(x)=0\) 是 \(x\) 为必败态的充要条件,\(SG(x)\neq 0\) 是 \(x\) 是必胜态的充要条件

以上就是一维 \(SG\) 函数的基本内容,利用这个例子我们可以解决一个简单的经典小问题

巴什博弈:一堆石子有 \(n\) 个,每次可以取 \([1,m]\) 个石子。先取完者获胜。问何时先手必胜,何时后手必胜?

发现暴力算是可以解决的

如果追求效率,经常不会真的去暴力算 \(SG\),而是考虑直接归纳每个状态的 \(SG\) 函数

如果算一遍 \(SG\) 的代价可以承受,就可以考虑直接去算

但是如果不能归纳出 \(SG\) 函数,这种博弈问题我们就是不能得出结论通解的

先介绍一下考场实用的一种方法:打表

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;

int n,m;
int SG[N];
bitset<N> S,clr;
void get_SG(){
    SG[0]=0;
    for(int i=1;i<=n;i++){
        S&=clr;
        for(int j=1;j<=min(m,i);j++){
            S[SG[i-j]]=1;
        }
        for(int j=0;;j++){
            if(!S[j]) {SG[i]=j;break;}
        }
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    get_SG();
    for(int i=1;i<=n;i++) cout<<SG[i]<<endl;
}

根据定义,这份代码可以打出巴什博弈的 \(SG\) 函数

我们可以观察出 \(SG\) 在巴什博弈的规律是 \(SG(x)=x\bmod (m+1)\),这是我们观察出的结论

一般我们可以打出表来,然后再证明(当然不证也不是啥大事,只要你多看看确认是对的就行)

巴什博弈的证明是显然的,就不赘述了

SG定理

如果 \(SG\) 只能解决一维的博弈问题,那这个玩意其实有点弱了

nim游戏

先用一个简单的例子来引入一下

nim game

这是一个可拆分的组合游戏

我们考虑一个子游戏,即考虑拿出任意一堆来做游戏

我们称 \(SG(x)\) 表示大小为 \(x\) 的堆的 \(SG\) 函数,显然有 \(SG(x)=x\)

我们很容易得出面前的每一堆的子游戏是必胜还是必败

尝试用子问题的胜败推出总问题的胜败!

这里先考虑两个子问题的复合,因为多个子问题本质上就是两个的情况的扩展

  • 当我面前两个游戏都是必败的,则我必败

      必败的走一步必然走到必胜的
    
      等会对面再把另一个变成必败的,我又收到了两个必败游戏
    
      最后我会收到两个无子可动的终盘游戏,则我必败
    
  • 假如我面前摆着一败一胜的游戏,实际上我必胜

      必胜的走一步能走到必败的
    
      对面会收到两个必败,对面一定会输
      
      故我必胜
    
  • 假如面前是两个必胜......

等会,好像不太对劲

两个必胜在我面前时,我根本无法判断我必胜还是必败

有点悲哀,好像寄了,单个的 \(SG\) 函数貌似无法直接复合生成,那难道没有办法了吗

SG定理

此时某位我不知道是谁的伟大的人给出了一个伟大的定理,SG定理

SG定理:游戏的“和”(其实是复合)的 \(SG\) 值是各个子问题 \(SG\) 的异或和

然后我们可以发现这个问题我们解决了!

于是nim游戏的 \(SG\) 已经非常简单的搞定了

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n;
int a[N];
int T;
void work(){
    cin>>n;
    int xr=0;
    for(int i=1;i<=n;i++)cin>>a[i],xr^=a[i];
    if(xr==0) cout<<"No\n";
    else cout<<"Yes\n";
}
int main(){
    cin>>T;
    while(T--){
        work();
    }
}

短小精悍

证明不会

标签:函数,必胜,必败,ICG,int,SG,游戏
From: https://www.cnblogs.com/exut/p/18516666

相关文章

  • 【SQL】Hive/Spark SQL笔记之时间函数、环比/同比/时间比较计算
    获取当天:'${zdt.format("yyyy-MM-dd")}'//获取上月月末select'${zdt.lastMonth().format("yyyy-MM-dd")}'T-1上月末select'${zdt.addDay(-1).lastMonth().format("yyyyMMdd")}'1个小时前select'${zdt.addHour(-1)......
  • 【C++】踏上C++学习之旅(四):细说“内联函数“的那些事
    文章目录前言1."内联函数"被创造出来的意义2.内联函数的概念2.1内联函数在代码中的体现2.2普通函数和内联函数的汇编代码3.内联函数的特性(重点)4.总结前言本章来聊一聊C++的创作者"本贾尼"大佬,为什么要创作出内联函数,以及内联函数的定义和内联函数的实现机制等......
  • Typora+gitee+picgo突然失效,此前Typora里面的图片image load failed,图片是gitee链接
    Typora+gitee+picgo突然失效,此前Typora里面的图片imageloadfailed,图片是gitee链接单纯把http链接复制粘贴到网页可以打开图片,但在Typora里面就是加载失败尝试解决方法如下:1、怀疑是Typora版本问题从用了几年的TyporaV1.02版本更新到最新的V1.9版本,发现所有图片又全都......
  • 轻松绕过AI检测!BypassGPT让你的AI文本变得更“人性化”
    摘要:BypassGPT是一款免费在线工具,可以将AI生成的内容转化为人类风格,轻松绕过GPTZero等检测系统。操作简单,让你的文本更自然、真实。最近我发现了一个非常实用的小工具,叫BypassGPT,它简直就是专为我们这些用AI生成内容的人量身打造的!如果你也经常写文章、做报告,或是需要生成一些自......
  • 介绍一下for break continue 函数(c基础)
    for函数是循环函数格式for(   expression1      ;    expression2    ;     expression3   )+  一种语句expression1初始化变量的值                                         ......
  • qsort函数的学习与使用
    零.导言    在之前的文章中,我介绍了冒泡排序,即按ASCII码值把元素从小到大排序(文章链接我放在了第五部分,有兴趣的小伙伴可以求看看)。而今天我将继续介绍qsort函数,这个函数可以起到和冒泡排序一样的作用,并且有着更加广泛的应用场景。一.qsort函数的定义    qso......
  • Mysql梳理11——聚合函数
    Mysql梳理11——聚合函数Mysql梳理11——聚合函数11.1引言11.2聚合函数介绍11.2.1什么是聚合函数11.2.2聚合函数类型11.2.3聚合函数语法11.3具体聚合函数11.3.1AVG和SUM函数11.3.2MIN和MAX函数11.3.3COUNT函数11.4GROUPBY11.4.1基本使用11.4.2使用多个列......
  • [C++] 成员函数指针和函数指针
    #include<iostream>usingnamespacestd;classA{public:A(inta):_a(a){}int_a;void*get_ptr(){returnthis;};voidp(intc){cout<<"_a:"<<_a<<endl;};};intint_func(inta,floatb){......
  • 2024年模型构建、多维空间与几何学国际学术会议(ICMBMSG 2024)
    2024年模型构建、多维空间与几何学国际学术会议(ICMBMSG2024)2024InternationalConferenceonModelBuilding,MultidimensionalSpaceandGeometry(ICMBMSG2024)会议信息大会官网:2024年模型构建、多维空间与几何学国际学术会议(ICMBMSG2024)投稿邮箱:[email protected]【......
  • 条件函数
    1.CASE函数    计算测试表达式CASE测试表达式WHEN简单表达式1THEN结果表达式1WHEN简单表达式2THEN结果表达式2…WHEN简单表达式nTHEN结果表达式n[ELSE结果表达式n+1]END    搜索表达式CASEWHEN......