公平组合游戏ICG
若一个游戏满足:
- 由两名玩家交替行动;
- 在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
- 不能行动的玩家判负;
则称该游戏为一个公平组合游戏。
NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。
NIM游戏
给定N堆物品,第i堆物品有Ai个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但不能不取。取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。
我们把这种游戏称为NIM博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。
所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。
NIM博弈不存在平局,只有先手必胜和先手必败两种情况。
先手必胜状态:可以走到某一个必败状态
先手必败状态: 走不到任何一个必败状态
定理: NIM博弈先手必胜,当且仅当 A1 ^ A2 ^ … ^ An != 0
SG函数
Mex运算
取集合中不存在的最小元素
SG函数定义
SG(终点)=0;
S
G
(
x
)
=
SG(x)=
SG(x)=MEX {
S
G
(
y
1
)
,
S
G
(
y
2
)
,
S
G
(
y
3
)
.
.
.
.
.
.
S
G
(
y
n
)
SG(y_1),SG(y_2),SG(y_3)... ...SG(y_n)
SG(y1),SG(y2),SG(y3)......SG(yn)}
当 S G ( x ) = 0 SG(x)=0 SG(x)=0时,必败,因为其余非0状态都可以一步到达0状态,把这个状态丢给对手
多个有向图问题
每一个有向图对应一个问题,实际中的博弈题往常对应多个有向图
当多个有向图SG函数异或值为0时,必败,反之必胜
即
S
G
(
x
1
)
⨁
S
G
(
x
2
)
⨁
S
G
(
x
3
)
⨁
.
.
.
.
.
.
⨁
S
G
(
x
n
)
=
0
SG(x_1) \bigoplus SG(x_2) \bigoplus SG(x_3)\bigoplus......\bigoplus SG(x_n)=0
SG(x1)⨁SG(x2)⨁SG(x3)⨁......⨁SG(xn)=0时,必败
例题
acwing893
code:
#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define ll long long
inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10+'0');}
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0)
#define ull unsigned long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define all(a) a+1,a+n+1
#define ALL(a) a.begin(),a.end()
// #define debug(a) cout <<#a << "=" << a << endl;
const int INF = 0x3f3f3f3f;
const ll LINF = 1ll<<60;
const int mod=1e9+7;
#define TT int T;cin>>T;while(T--)
void dbg_out() { cerr << endl; }
template<typename Head, typename... Tail> void dbg_out(Head H, Tail... T) { cerr << ' ' << H; dbg_out(T...); }
#define debug(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__);
int n,k,f[10010],s[10010];
int sg(int x){
if(f[x]!=-1) return f[x];
unordered_set<int> S;
rep(i,1,k){
int sum=s[i];
if(x>=sum) S.insert(sg(x-sum));
}
for(int i=0;;i++)
if(!S.count(i))
return f[x]=i;
}
int main(){
cin>>k;
rep(i,1,k) cin>>s[i];
memset(f,-1,sizeof f);
cin>>n;
int res=0;
int x;
rep(i,1,n){
cin>>x;
res^=sg(x);
}
if(res) cout<<"Yes\n";
else cout<<"No\n";
return 0;
}