-
1、Nim游戏
- 对于博弈论问题通常都是签到题,说难贼难,说简单一般三行代码就搞定,下面介绍一个最经典的博弈论问题,Nim游戏问题
- 公平组合游戏(ICG)游戏
- 先拿两堆石子举例子,如果说是2 3,那么先手必胜,因为先手只需要拿第二堆里面的一个后面根据后手镜像拿就好了
- 必败状态:0 0
- 必胜状态:先手拿完剩下的状态一定是必败状态该状态就是必胜状态
- 定理一:对于n堆石子,如果有 a1 ^ a2 ^ … ^ an 等于0则称该状态是必败状态,反之为必胜状态
- 1 ^ 2 ^ 3也等于0,也是先手必败状态,不一定完全相等,但会变成相等状态,此时轮到先手开始,所以先手必败(会变成2 2)
- 代码
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int res=0;
while(n--)
{
int x;
cin>>x;
res^=x;
}
if(res)puts("Yes");
else puts("No");
return 0;
}
-
2、台阶—Nim游戏
- 如果奇数台阶上的异或和为0则先手必败,反之先手必胜,因为例如 3 1 2
- 异或和不为0,先手只要第一步将两相邻奇之间保持石子相等即(2 2 2)下次如果对手移动奇数石子,则先手移动另一个相邻相等的奇数位的台阶的石子,要是对手移动偶数位的石子,则依次向下移动即可因为后手可以移动先手一定可以移动
- 代码
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int res=0;
if(n==1)
{
puts("Yes");return 0;
}
while(n--)
{
int x;
cin>>x;
if(n&1)
res^=x;
}
if(res)puts("Yes");
else puts("No");
return 0;
}
-
3、SG函数(集合Nim游戏)
- 终止状态定义为0,从后往前倒序更新mex值就是SG函数的值(mex定义为集合中不存在的最小元素的值,像mex{1 2 3}=0,mex{0 1 2 3 4}=5)
- 对于SG函数函数值的求法通常采用记忆化搜索
- 代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;
const int N=110,M=10010;
int s[N],SG[M];
int n,m;
int sg(int x)
{
if(SG[x]!=-1)return SG[x];
unordered_set<int>hash;
for(int i=0;i<m;i++)
{
int sum=s[i];
if(x>=sum)hash.insert(sg(x-sum));
}
for(int i=0;;i++)
{
if(!hash.count(i))
return SG[x]=i;
}
}
int main()
{
cin>>m;
for(int i=0;i<m;i++)cin>>s[i];
cin>>n;
memset(SG,-1,sizeof SG);
int res=0;
while(n--)
{
int x;
cin>>x;
res^=sg(x);
}
if(res)puts("Yes");
else puts("No");
return 0;
}
-
4、拆分Nim游戏
- 一堆石子可以拆分成两堆更小的石子,同样的求SG函数值,并把所有的堆异或起来,不等于0则先手必胜,反之先手必败
- 利用SG函数的性质:SG(i,j)=SG(i) ^ SG(j)
- 每一个局面的SG的值等于后面所有局面SG值的异或和
- 代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;
const int N=110,M=10010;
int s[N],SG[M];
int n;
int sg(int x)
{
if(SG[x]!=-1)return SG[x];
unordered_set<int>hash;
for(int i=0;i<x;i++)
{
for(int j=0;j<=i;j++)
hash.insert(sg(i)^sg(j));
}
for(int i=0;;i++)
if(!hash.count(i))
return SG[x]=i;
}
int main()
{
cin>>n;
memset(SG,-1,sizeof SG);
int res=0;
while(n--)
{
int x;
cin>>x;
res^=sg(x);
}
if(res)puts("Yes");
else puts("No");
return 0;
}