最近刚刚接触了基础博弈论 一开始感觉是一个非常难以理解的专题 看了几天别人的题解 写了几道题感觉稍微有了一点点的感觉 下面来总结一下几个常见的基础博弈
1.巴什游戏
最简单的那当然就是巴什游戏了题目是说有一堆石子A,B分别去取石子每一次可取的数目为1—m个最后没有石子可以去的人则为败方那么思路很简单 先想当有m+1个石子时候不论先手取多少个 后手一定可以一次性取完,所以后手一定赢 那么再思考如果有n*(m+1)个我们都可以让先后手保持这样的状态 先手取k个后手取m+1-k个保证每一个周期结束后数目仍然是m+1的倍数直到m+1时候 先手取完后 后手全部取完 如果不是m+1的倍数那么先手只需要一定数目的石子让其变为m+1的倍数即可 所以有当n%(m+1)==0先手必败n%(m+1)!=0先手必胜
2.NIm游戏
规则像是巴什游戏的升级版 有n个石子堆每一个人能对任意一个堆任意个石子(不能为0)最后没有石子可取的人败这里需要用到数学知识推理(我不会直接给出结论)
设sum[]记录每一个堆的数量那么如果 sum[1]^sum[2].....^sum[n]==0先手必败 否则先手必胜
3.威佐夫游戏
这个游戏是说有两个石子堆玩家可以对一个石子堆取任意石子也可以对两个石子堆取相同数目的石子最后没有石子可取的人败
这里我们需要用到黄金比例
double gold=(1+sqrt(5))/2;
两个堆的数量之差乘黄金比例如果等于较小的那个数量则先手必败否则先手胜
4.反NIm游戏
规则是一样的只是说最后没石子拿的人胜利 那么结论也相似 如果全部石子堆的数量都是1那么需要判断奇偶性 奇数后手胜 偶数先手胜 如果有一个或多个数量大于一的石子堆 那么还是取异或和如果 sum[1]^sum[2].....^sum[n]==0先手必败否则先手必胜
5.SG函数和Mex
Mex是对一个集合的运算 是求不等于集合中任意一个数的最小非负整数如Mex{1,2,3}=0;
SG=0时是必败点 其他为必胜点 所有的必败点都可以指向必胜点一定不会再指向必败点 而所有的必胜点都会指向必败点 SG函数就是对一个结点的所有后继点进行SG运算 比如已知SG[0]=0;
SG[1]=1; SG[2]=2;如果结点3指向1,2,0那么SG[3]=3;对于不同的游戏SG函数有不同的算法
如巴什游戏中
因为可以取1-m个石子所以n的后继结点(对n进行一次操作)为n-m,n-m-1,n-m-2,n-m-3.....n-1;
而每一个结点又有自己的后继结点
用SG解决巴什游戏代码如下
#include<iostream>
#include<cstring>
using namespace std;
int s[1000],sg[1000];//s记录每一个结点的后继结点的sg每循环一次都要清空一次
bool bashi(int n,int m)
{
return n%(m+1)==0;
}
void getSG(int n,int m)
{
memset(sg,0,sizeof(sg));
for(int i=1;i<=n;i++)
{
memset(s,0,sizeof(s));
for(int j=1;j<=m&&i-j>=0;j++)
{
s[sg[i-j]]=1;
}
for(int j=0;j<=n;j++)
{
if(!s[j])
{
sg[i]=j;
break;
}
}
}
}
int main()
{
int n,m;
while(cin>>n>>m)
{
int ans=bashi(n,m);
if(ans)cout<<"后手
";
else cout<<"先手
";
getSG(n,m);
if(sg[n])
cout<<"先手
";
else
cout<<"后手
";
}
}
6.MUlti-SG
如果一个游戏可以再分成多个独立的游戏那么这个游戏的SG就是它分出独立的游戏的SG异或和再进行Mex运算
如 在NIM游戏上多加一个操作即玩家可以选择拿走任意石子或者把大于2的石子分为两堆那么这里分为两堆就是变成了两个独立的游戏就用到了Multi—SG 代码如下
#include<iostream>
#include<cstring>
using namespace std;
int sg[1000],s[1000];
void getsg()
{
memset(sg,0,sizeof(sg));
sg[0]=0;
sg[1]=1;
sg[2]=2;
for(int i=3;i<=1000;i++)
{
memset(s,0,sizeof(s));
for(int j=0;j<=1000&&j<=i;j++)
{
s[sg[i-j]]=1;
3s[(sg[j]^sg[i-j])]=1;
}
for(int j=0;j<1000;j++)
{
if(!s[j])
{
sg[i]=j;
break;
}
}
}
}
int main()
{
int n;
getsg();
while(cin>>n)
{
int a[100];
int sum=0;
for(int i=0;i<n;i++)
{
cin>>a[i];
sum^sg[a[i]];
}
if(sum==0)cout<<"0"<<endl;
else cout<<"1"<<endl;
}
}
对于SG函数一开始我也是很不理解多做了一些的题目发现其实一个堆代表一个独立的游戏 而每一个堆的数量就是最高的SG结点而根据操作(仅操作一次)一开始的n会变为其他数量m,k,l,这些就是n的后继结点而在操作一次数量会再次变化这些就是m,k,l的后继结点 因此不同的题目SG的算法不同
7.规律型博弈
这种博弈我认为是我们先找到一个必胜点作为目标状态(如巴什游戏中的n*(m+1))然后当时其他状态的时候通过操作转为目标状态再加以分析题目就会简单明了许多