必败点(P点):上一个选手(Previous player)将取胜的位置成为必败点。
必胜点(N点):下一个选手(Next player)将取胜的位置成为必胜点。
所有终结点是必败点(P点);
从任何必胜点(N点)操作, 至少有一种方法可以进入必败点(P点);
无论如何操作,从必败点(P点)都只能进入必败点(P点)。
步骤1:将所有终结位置标记为必败点(P点);
步骤2:将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点),则将该点标记为必败点(P点);
步骤4:如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2。
NIM游戏是组合博弈中的经典游戏。游戏场景如下:有若干堆石子,每堆石子的数量都是有限的,游戏双方轮流从某一堆中取走任意数量的石子(至少取1个),取走最后一个石子的玩家获胜。
设游戏中有 nnn 堆石子,各堆石子数分别为 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an。将这些石子数都表示成二进制形式。
定义NIM - 和为 a1⊕a2⊕⋯⊕ana_1\oplus a_2\oplus\cdots\oplus a_na1⊕a2⊕⋯⊕an(⊕\oplus⊕ 表示按位异或运算)。
定理:NIM游戏中,当且仅当 a1⊕a2⊕⋯⊕an=0a_1\oplus a_2\oplus\cdots\oplus a_n = 0a1⊕a2⊕⋯⊕an=0 时,当前局面为必败局面;当 a1⊕a2⊕⋯⊕an≠0a_1\oplus a_2\oplus\cdots\oplus a_n\neq0a1⊕a2⊕⋯⊕an=0 时,当前局面为必胜局面。
证明
终结局面:当所有堆石子数都为 000 时,0⊕0⊕⋯⊕0=00\oplus0\oplus\cdots\oplus0 = 00⊕0⊕⋯⊕0=0,此时是必败局面。
必胜局面可转化为必败局面:若 a1⊕a2⊕⋯⊕an=k≠0a_1\oplus a_2\oplus\cdots\oplus a_n = k\neq0a1⊕a2⊕⋯⊕an=k=0,则 kkk 的二进制表示中至少有一位为 111。在 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an 中一定存在一个 aia_iai,其对应的二进制位上也为 111。通过从第 iii 堆中取走一定数量的石子,使得取走后这堆石子数变为 ai′a_i'ai′,满足 a1⊕a2⊕⋯⊕ai−1⊕ai′⊕ai+1⊕⋯⊕an=0a_1\oplus a_2\oplus\cdots\oplus a_{i - 1}\oplus a_i'\oplus a_{i+1}\oplus\cdots\oplus a_n=0a1⊕a2⊕⋯⊕ai−1⊕ai′⊕ai+1⊕⋯⊕an=0。
必败局面只能转化为必胜局面:若 a1⊕a2⊕⋯⊕an=0a_1\oplus a_2\oplus\cdots\oplus a_n = 0a1⊕a2⊕⋯⊕an=0,假设从第 iii 堆取走石子后变为 ai′a_i'ai′,那么 a1⊕a2⊕⋯⊕ai−1⊕ai′⊕ai+1⊕⋯⊕an≠0a_1\oplus a_2\oplus\cdots\oplus a_{i - 1}\oplus a_i'\oplus a_{i+1}\oplus\cdots\oplus a_n\neq0a1⊕a2⊕⋯⊕ai−1⊕ai′⊕ai+1⊕⋯⊕an=0。因为异或运算的性质决定了,改变其中一个数后,异或和必然改变。
对于一个组合游戏的每个状态 xxx,定义其SG函数值 SG(x)SG(x)SG(x)。
计算方法:
首先,对于所有的终结状态 xxx,SG(x)=0SG(x)=0SG(x)=0。
对于非终结状态 xxx,设 xxx 能通过一步操作到达的所有状态为 y1,y2,⋯ ,yky_1,y_2,\cdots,y_ky1,y2,⋯,yk,则 SG(x)=mex({SG(y1),SG(y2),⋯ ,SG(yk)})SG(x)=mex(\{SG(y_1),SG(y_2),\cdots,SG(y_k)\})SG(x)=mex({SG(y1),SG(y2),⋯,SG(yk)}),其中 mexmexmex 函数是求一个集合中未出现的最小非负整数。例如,若集合为 {0,1,3}\{0,1,3\}{0,1,3},则 mex({0,1,3})=2mex(\{0,1,3\}) = 2mex({0,1,3})=2。
作用:
定义:组合游戏的并是指将多个组合游戏同时进行的一种游戏形式。假设有 kkk 个组合游戏 G1,G2,⋯ ,GkG_1, G_2, \cdots, G_kG1,G2,⋯,Gk,组合游戏的并 G=G1+G2+⋯+GkG = G_1 + G_2 + \cdots + G_kG=G1+G2+⋯+Gk。在游戏 GGG 中,玩家的一步操作是在 G1,G2,⋯ ,GkG_1, G_2, \cdots, G_kG1,G2,⋯,Gk 中的某一个游戏中进行一步合法操作。
SG 定理:对于组合游戏的并 G=G1+G2+⋯+GkG = G_1 + G_2 + \cdots + G_kG=G1+G2+⋯+Gk,其某状态的 SGSGSG 值等于各个子游戏在相应状态下 SGSGSG 值的异或和,即 SG(G)=SG(G1)⊕SG(G2)⊕⋯⊕SG(Gk)SG(G)=SG(G_1)\oplus SG(G_2)\oplus\cdots\oplus SG(G_k)SG(G)=SG(G1)⊕SG(G2)⊕⋯⊕SG(Gk)。这个定理非常重要,它使得我们可以通过分别计算各个子游戏的 SGSGSG 值,再求异或和来判断组合游戏并的胜负情况。
以下是一个简单示例代码,展示如何利用 SG 定理计算组合游戏的并的胜负情况(假设已有 sg 函数用于计算单个游戏状态的 SGSGSG 值)
#include
using namespace std;
// 假设已经有了计算单个游戏 SG 值的函数 sg
int sg(int state) {
// 具体实现省略,根据实际游戏规则编写
return 0;
}
计算组合游戏的并的 SG 值
int combinedSG(int numGames, int* states) {
int result = 0;
for (int i = 0; i < numGames; ++i) {
result ^= sg(states[i]);
}
return result;
}
// 判断胜负示例函数
void determineWinner(int numGames, int* states) {
int combinedSgValue = combinedSG(numGames, states);
if (combinedSgValue == 0) {
cout << "当前局面为必败局面" << endl;
}
else {
cout << "当前局面为必胜局面" << endl;
}
}
int main() {
int numGames = 3; // 假设有 3 个子游戏
int states[3] = {1, 2, 3}; // 各个子游戏的状态
determineWinner(numGames, states);
return 0;
}