博弈论+ybt题解

NIM游戏及其证明

题目描述即为T1,不多赘述

有向图游戏及SG函数

而对于由n个有向图游戏组成的组合游戏,设它们的起点分别为S1,S2,,Sn,则有定理:

当且仅当SG(s1)SG(s2)SG(sn)0时,这个游戏是先手必胜的。

考虑如何证明,我们考虑NIM游戏中一种合法的取法为选择一堆并取走其中任意大于0的石子数也就是可以从 ai 转移到 [ai1,0] 的任意值,并且NIM游戏时通过当所有 ai 都为0时游戏为先手必输而推来的,如果满足这个两个条件就可以沿用NIM游戏的定理

我们考虑 SG(x) 的定义,就是与 x 直连的所有后继的 mex,所以 SG(x) 可以直接转移到 [SG(x)1,0],并且当所有 SG(ai) 都为0时,游戏也是先手必输局面,这显然也满足NIM游戏的定义,所以可以直接沿用NIM游戏的证明,证毕

同时有意思的一点是,NIM游戏也可以用SG函数来解释,我们有k堆相当于有k个起点,然后对于每个堆可以拿走任意个的限制就是对于 ai,它所对应的节点 [ai1,0] 都有一条有向边,所以 SG(ai)=ai,也恰恰符合了定义

T1:

根据NIM游戏直接做

T2:

  1. n=1,那先手就不能进行操作,所以无必胜策略。

  2. n=2,先手有必胜策略。先手进行操作2,让后手陷入情况1。

  3. n是奇数(n1),先手有必胜策略。先手进行操作1,因为一个数的本身也是它的因子,而且它是个奇数,所以可以除以它本身,让后手陷入情况1。

  4. n是偶数(n2),偶数肯定不能进行操作2,因为偶-1=奇,让后手变成情况3,后手就赢了。

首先将n表示成如下式子:

n=2k×m

其中m为奇数,继续分情况考虑。

4.1 m=1,也即n=2k

因为2k没有任何奇因子(除1外),所以只能进行操作2,后手就赢了。所以无必胜策略。

4.2 m是个质数

m是个质数时,操作1只能除以m,剩下2k

4.2.1. k=1

则除完m后只剩下2,让后手变成情况2,后手就赢了,无必胜策略。

4.2.2. k>1

让后手陷入情况4.1,先手赢,有必胜策略。

4.3 m不是个质数

也即:

n=2k×p1a1×p2a2××pmam(m>1)

4.3.1 k=1

此时只要除以p1a1×p2a2××piai××pmam×pi1 (1im)就行了,

剩下 2×pi 让后手陷入情况4.2.1,先手赢,有必胜策略。

4.3.2. k>1

除以 p1a1×p2a2××pmam,剩下2k,让后手陷入情况4.1,先手赢,有必胜策略。

所以情况4.3都有必胜策略。

T3:

求SG函数练习题

首先我们把开始局面看成是一种状态,发现可以对于每一个 ai 求一下SG函数,然后再把它们异或起来就是这种局面的SG函数(ps:这是另一个定理,但是ybt中并没有讲到,并且也没有提到证明,本人也尝试证明过,但没有证明出来)

然后我们递归下去记搜SG函数即可,复杂度 O(n2)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int n;
int a[N],sg[N];
int SG(int x){
    if(sg[x]!=-1)  return sg[x];
    int k=0;
    bool vis[x+50];\\注:这里vis只能为一个函数服务,所以要写在函数内部
    memset(vis,0,sizeof(vis));
    for(int i=1;i*i<=x;i++){
        if(x%i)  continue;
        k^=SG(i);
        if(i*i==x||i==1)  continue;
        k^=SG(x/i);
    }
    for(int i=1;i*i<=x;i++){
        if(x%i)  continue;
        vis[k^SG(i)]=1;
        if(i*i==x||i==1)  continue;
        vis[k^SG(x/i)]=1;
    }
    int res=0;
    while(vis[res])  res++;
    return sg[x]=res;
}
int main(){
    memset(sg,-1,sizeof(sg));
    sg[1]=0;
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int k=0;
        for(int i=1;i<=n;i++){
            k^=SG(a[i]);
        }
        if(k!=0)  printf("freda\n");
        else  printf("rainbow\n");
    }
    return 0;
}

T4:

硬控我一整天!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

思路很清晰明了,记忆化搜索出SG函数

本人问题:

1.为什么不可以直接把胜负反过来做?

因为考虑SG定理的证明,一个是任意一个是存在,反过来就不行了

2.为什么不可以设 1n,n1 为1转移?

起始状态不可设为1,不满足定理

3.1*1为什么不可以看成一个必败状态,直接进行转移?

待解答ing。。。

posted @   daydreamer_zcxnb  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示