最优博弈:Nim游戏,SG函数
Posted 一只特立独行的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最优博弈:Nim游戏,SG函数相关的知识,希望对你有一定的参考价值。
公平组合游戏ICG
(1)有两个玩家,游戏规则对两个玩家公平
(2)游戏状态有限,能走的步数也是有限的
(3)轮流走,当一个玩家不能走时游戏结束
(4)游戏的局势不能区分玩家的身份,例如黑白棋就是不行的
特征:
给定初始局势,指定先手玩家,如果双方都采取最优策略,那么获胜者已经确定了,也就是说ICG问题存在必胜策略。
常见ICG裸题:
巴什游戏
尼姆游戏
图游戏和SG函数
威佐夫游戏
Nim游戏:
有n堆石子,第i堆石子的数量是
a
i
a_i
ai, Alice和Bob可以从任意一堆石子中拿走任意石子。Alice先拿,双方都走最优策略,谁最终会胜利?
结论:
a
1
a_1
a1 ^
a
2
a_2
a2 ^
.
.
.
...
... ^
a
n
a_n
an == 0
−
−
>
-->
−−> 先手必败
a
1
a_1
a1 ^
a
2
a_2
a2 ^
.
.
.
...
... ^
a
n
a_n
an != 0
−
−
>
-->
−−> 先手必胜
证明:
首先,0^0^…^0=0,为先手必败。
情况1:
a
1
a_1
a1 ^
a
2
a_2
a2 ^
.
.
.
...
... ^
a
n
a_n
an == 0
情况2:
a
1
a_1
a1 ^
a
2
a_2
a2 ^
.
.
.
...
... ^
a
n
a_n
an == x(x!=0)
情况1在任意一个
a
i
a_i
ai改变后,必然会转移到情况2。、
情况2一定可以转移到情况1:
对于x的最高位的1,一定在
a
i
a_i
ai中找到一个在同样的位为1数
a
k
a_k
ak。并且由于
a
k
a_k
ak 和x的最高位都是1,所以
a
k
a_k
ak ^ x <
a
k
a_k
ak。于是可以拿走
(
a
k
−
a
k
(a_k-a_k
(ak−ak ^x)个石子,使得
a
k
a_k
ak变成
a
k
a_k
ak^x。
a
1
a_1
a1 ^
a
2
a_2
a2 ^
.
.
.
...
... ^
a
n
a_n
an ^x=x^x=0
所以一定可以由条件2转移到条件1。
结论
由于条件1在经过任意操作后都会变成条件2,条件2在经过一定变换一定能变成条件1。依次操作后,首先到条件2的玩家一定获得胜利。所以直接判断初始条件即可。
acwing题库:Nim游戏
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
int sum=0;
while(n--){
int k;
cin>>k;
sum^=k;
}
if(sum)puts("Yes");
else puts("No");
return 0;
}
SG函数
Nim游戏博弈给出了Nim游戏的一般解法。但是题目技巧性太强,约束条件较少,不适合进行一般解的推广。所以通过SG函数来进行博弈游戏一般性的推广。
定义运算:
sex(x):返回不属于集合x的最小正整数。
SG(x)={ sex(
y
1
y_1
y1) , sex(
y
2
y_2
y2) , …,sex(
y
n
y_n
yn) }
y是x在经过一次操作后可以转移到的状态。
结论:
S
G
(
a
1
)
SG(a_1)
SG(a1) ^
S
G
(
a
2
)
SG(a_2)
SG(a2) ^
.
.
.
...
... ^
S
G
(
a
n
)
SG(a_n)
SG(an) == 0
−
−
>
-->
−−> 先手必败
S
G
(
a
1
)
SG(a_1)
SG(a1) ^
S
G
(
a
2
)
SG(a_2)
SG(a2) ^
.
.
.
...
... ^
S
G
(
a
n
)
SG(a_n)
SG(an) != 0
−
−
>
-->
−−> 先手必胜
证明同Nim函数。
acwing题库:集合-Nim游戏
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
const int N = 105,M = 10005;
int k,s[N],n,f[M];
int sg(int x){
if(f[x]!=-1)return f[x];//记忆递归减少运算
set<int > se;
for(int i=0;i<k;i++){
if(s[i]<=x){
se.insert(sg(x-s[i]));
}
}
for(int i=0;;i++){//相当于sex函数
if(se.count(i)==0){
return f[x]=i;
}
}
}
int main(){
memset(f,-1,sizeof f);
cin>>k;
for(int i=0;i<k;i++){
cin>>s[i];
}
int res=0;
cin>>n;
for(int i=0;i<n;i++){
int h;
cin>>h;
res^=sg(h);
}
if(res==0) puts("No");
else puts("Yes");
return 0;
}
以上是关于最优博弈:Nim游戏,SG函数的主要内容,如果未能解决你的问题,请参考以下文章