博弈论+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:
-
\(n=1\),那先手就不能进行操作,所以无必胜策略。
-
\(n=2\),先手有必胜策略。先手进行操作2,让后手陷入情况1。
-
若\(n\)是奇数(\(n \neq 1\)),先手有必胜策略。先手进行操作1,因为一个数的本身也是它的因子,而且它是个奇数,所以可以除以它本身,让后手陷入情况1。
-
若\(n\)是偶数(\(n \neq 2\)),偶数肯定不能进行操作2,因为偶-1=奇,让后手变成情况3,后手就赢了。
首先将\(n\)表示成如下式子:
其中\(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\)不是个质数
也即:
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。。。