博弈论+ybt题解

NIM游戏及其证明

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

有向图游戏及SG函数

而对于由\(n\)个有向图游戏组成的组合游戏,设它们的起点分别为\(S_1, S_2, \ldots, S_n\),则有定理:

当且仅当\(\text{SG}(s_1) \oplus \text{SG}(s_2) \oplus \ldots \oplus \text{SG}(s_n) \neq 0\)时,这个游戏是先手必胜的。

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

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

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

T1:

根据NIM游戏直接做

T2:

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

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

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

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

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

\[n = 2^k \times m \]

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

4.1 \(m=1\),也即\(n = 2^k\)

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

4.2 \(m\)是个质数

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

4.2.1. \(k=1\)

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

4.2.2. \(k>1\)

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

4.3 \(m\)不是个质数

也即:

\[n = 2^k \times p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_m^{a_m} \quad (m>1) \]

4.3.1 \(k=1\)

此时只要除以\(p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_i^{a_i} \times \cdots \times p_m^{a_m} \times p_i^{-1}\) (\(1 \leq i \leq m\))就行了,

剩下 \(2 \times p_i\) 让后手陷入情况4.2.1,先手赢,有必胜策略。

4.3.2. \(k>1\)

除以 \(p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_m^{a_m}\),剩下\(2^k\),让后手陷入情况4.1,先手赢,有必胜策略。

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

T3:

求SG函数练习题

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

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

代码:

#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.为什么不可以设 \(1*n,n*1\) 为1转移?

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

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

待解答ing。。。

posted @ 2024-12-20 12:34  daydreamer_zcxnb  阅读(6)  评论(0编辑  收藏  举报