公平组合游戏
作者:互联网
公平组合游戏
\(Nim\) 游戏
概述与解法
\(Nim\) 游戏是 \(ICG(Impartial\ Combinatorial\ Games)\)
满足下列条件的游戏才算 \(ICG\) :
- 两个人
- 两个人交替对游戏进行移动,每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动
- 对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素
- 如果轮到某名选手移动,且这个局面的合法的移动集合为空(无法移动),则这名选手 \(lose\) 。(根据这个定义,很多日常的游戏并非 \(ICG\))
满足上述条件的游戏才可以使用之后叙述的 \(Nim\) 游戏的解法来解决
\(Nim\) 游戏及解法
通常的\(Nim\) 游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判\(lose\)(因为他此刻没有任何合法的移动)。
定义 \(P-position\) 和 \(N-position\) ,其中P代表 \(Previous\),\(N\) 代表 \(Next\)
直观的说,上一次 \(move\) 的人有必胜策略的局面是 \(P-position\),也就是“后手可保证必胜”或者“先手必败”,现在轮到 \(move\) 的人有必胜策略的局面是 \(N-position\),也就是“先手可保证必胜”
更严谨的定义是:1.无法进行任何移动的局面(也就是 \(terminal\ position\))是\(P-position\);2.可以移动到\(P-position\) 的局面是 \(N-position\);3.所有移动都导致\(N-position\)的局面是\(P-position\)。
\(Nim\) 游戏的结论:若是对于一个游戏局面 \(a_{1\sim n}\),它是 \(P-postion\) 当且仅当 \(a_1\oplus a_2\oplus \cdots \oplus a_n=0\)
这个证明也显然,若是满足 \(a_1\oplus a_2\oplus \cdots \oplus a_n=0\) ,则对于 \(a_{1\sim n}\) 其所有二进制位上的 \(1\) 有偶数个
也就是如果之后先手取一个那么后手就取对应的一个,最后总是先手的取不到
这样先手就输了
有向图游戏与 \(SG\) 函数
概述
大部分公平组合游戏都可以转换为有向图游戏,即:
- 在一个有向无环图中,只有一个起点,上面有一个棋子,两个玩家轮流沿着有向边推动棋子,不能走的玩家判 \(lose\)。
我们定义 \(mex(S)\) 函数:
\[mex(S)=\min\{x\}(x\in N,x\notin S) \]如:\(mex\{1,2,3\}=0,mex\{0,1,3\}=2\)
对于状态 \(x\) 和它所有的 \(k\) 个后继状态 \(y_{1\sim k}\),定义其 \(SG\) 函数:
\[SG(x)=mex\{SG(y_1),SG(y_2)\cdots SG(y_k)\} \]而对于有 \(n\) 个有向图组成的组合游戏,设其起点分别为 \(s_{1\sim n}\) ,则有定理:
当且仅当 \(SG(s_1)\oplus SG(s_2)\oplus \cdots \oplus SG(s_n)\neq 0\) 时先手必胜,同时,这是这一个组合游戏的游戏状态的 \(SG\) 值。
这一定理被称作 \(Sprague-Grundy(SG)\) 定理
可以把 \(SG\) 类比 \(Nim\) 来理解
而 \(Nim\) 游戏可以转化为一个有向图游戏用 \(SG\) 定理求解
习题
P2197 【模板】nim 游戏
#include<bits/stdc++.h>
using namespace std;
#define re register
inline int read(){
re int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
signed main(){
re int T=read();
while(T--){
re int n=read(),ans=0;
for(re int i=1;i<=n;++i) ans^=read();
puts(ans?"Yes":"No");
}
}
P1247 取火柴游戏
思路:
这题多了一个输出方案
按照 \(Nim\) 游戏的解法,我们先求出 \(chk=a_1\oplus a_2\oplus \cdots \oplus a_n\)
若 \(chk=0\),即 \(P-position\),则先手必输
否则,我们考虑让电脑在我们取完第一次之后变为 \(P-positon\)
也就是让某一个 \(a_i\) 变为 \(chk\oplus a_i\),这样就可以达到目的
判断一下 \(chk\oplus a_i\) 是否大于等于 \(a_i\) 就行 (不能不取,也不能拿负数个)
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n;
int a[N];
signed main(){
n=read();
int chk=0;
for(int i=1;i<=n;++i){
a[i]=read();
chk^=a[i];
}
if(!chk){
puts("lose");
return 0;
}
for(int i=1;i<=n;++i){
if((chk^a[i])>=a[i]) continue;
printf("%d %d\n",a[i]-(chk^a[i]),i);
a[i]=chk^a[i];
for(int j=1;j<=n;++j) printf("%d ",a[j]);
break;
}
}
标签:ch,游戏,组合,Nim,公平,position,oplus,SG 来源: https://www.cnblogs.com/into-qwq/p/16473469.html