博弈论是算法竞赛中数学部分的一个分支,在各种acm竞赛中也经常考。
本篇博客主要有以下内容:
1.博弈论的基本概念
具有竞争或对抗性质的行为称为博弈行为。在这类行为中,参加斗争或竞争的各方各自具有不同的目标或利益。为了达到各自的目标和利益,各方必须考虑对手的各种可能的行动方案,并力图选取对自己最为有利或最为合理的方案。博弈的本质是利益相关,而非利益冲突。
2.Nim游戏
2.1 Nim游戏的概念以及分析
给定N堆物品,第i堆物品有Ai个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但不能不取。取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。
我们把这种游戏称为NIM博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。
所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。
NIM博弈不存在平局,只有先手必胜和先手必败两种情况。
定理: NIM博弈先手必胜,当且仅当 A1 ^ A2 ^ … ^ An != 0
2.2例题
分析: NIM博弈先手必胜,当且仅当 A1 ^ A2 ^ … ^ An != 0
理由如下:游戏进行到终点时,A1=A2....=0,此时A1 ^ A2 ^ … ^ An = 0;
若A1 ^ A2 ^ … ^ An != 0,设A1 ^ A2 ^ … ^ An =x,x的二进制1的最高一位是第k位,则A1 ,A2 , … An 中必然存在一个数Ai,Ai的第k位是一。此时Ai^x<Ai,则每次找出Ai并从Ai中拿走(Ai-Ai^x)个,就能使A1 ^ A2 ^ … ^ An = 0。若A1 ^ A2 ^ … ^ An = 0,先手无论怎么操作都不能让
A1 ^ A2 ^ … ^ An = 0,如果另一方采用最优策略,则先手必败。
此题只要判断A1 ^ A2 ^ … ^ An 是否等于0即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,x,t;
signed main()
{
cin>>n;
while(n--)
{
cin>>x;
t=t^x;
}
if(t) puts("Yes");
else puts("No");
return 0;
}
3.sg函数
3.1Mex运算
设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即:
mex(S) = min{x}, x属于自然数,且x不属于S
3.2Sg函数
在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1, y2, …, yk,定义SG(x)为x的后继节点y1, y2, …, yk 的SG函数值构成的集合再执行mex(S)运算的结果,即:
SG(x) = mex({SG(y1), SG(y2), …, SG(yk)})
特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(s)。
4.sg函数例题
题目分析
设G1, G2, …, Gm 是m个有向图游戏。定义有向图游戏G,它的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步。G被称为有向图游戏G1, G2, …, Gm的和。
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数值的异或和,即:
SG(G) = SG(G1) ^ SG(G2) ^ … ^ SG(Gm)
定理:有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0。
有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。
ac代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[110],f[10010],res,m,n,x;
int sg(int x)
{
if(f[x]!=-1) return f[x];
unordered_set<int> s;
for(int i=1;i<=m;i++){
int d=a[i];
if(x>=d) s.insert(sg(x-d));
}
for(int i=0;;i++) if(!s.count(i)) return f[x]=i;
}
signed main()
{
cin>>m;
for(int i=1;i<=m;i++) cin>>a[i];
memset(f,-1,sizeof f);
cin>>n;
while(n--)
{
cin>>x;
res^=sg(x);
}
if(res) puts("Yes");
else puts("No");
return 0;
}