Nim游戏
题目描述
甲,乙两个人玩\(Nim\)取石子游戏。
\(Nim\)游戏的规则是这样的:地上有\(n\)堆石子\((\)每堆石子数量小于\(10000)\),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这\(n\)堆石子的数量,他想知道是否存在先手必胜的策略。
输入输出格式
输入格式:
第一行一个整数\(T \leq 10\),表示有T组数据
接下来每两行是一组数据,第一行一个整数\(n\),表示有\(n\)堆石子,\(n \leq10000\);
第二行有\(n\)个数,表示每一堆石子的数量
输出格式:
共\(T\)行,如果对于这组数据存在先手必胜策略则输出 Yes
,否则输出No
,不包含引号,每个单词一行。
今天刚学会的一点博弈论的基础,趁热打铁,赶紧巩固.
博弈论\(DP\)
\(Nim\)游戏的特征:
- 两人游戏
- 回合制游戏
- 某一方无法操作时为败
\(1\) 档
现在有一个游戏 \(G1\) , 有两个人 \(Alice,Bob\) 玩游戏,\(Alice\)先手,两人轮流 取石子,每次可以取走任意数量的石子 , 当某个时刻某人无法取石子时此人输,则\(Alice\)是必胜还是必败?
很容易可以看出\(Alice\)只要取走所有的石子,那么\(Bob\)必败,因此\(Alice\)必胜.下面用\(DP\)的方法解决这个问题.
- 设 \(f[i]\) 代表还剩 \(i\) 个时石头对当前先手是必胜还是必败
- 必胜 : \(f[i]=1\)
- 必败 : \(f[i]=0\)
- \(f[0]=0\),以为无石子可取
- \(f[i]\) 可以转移到 \(f[i-1],f[i-2],f[i-3],...\)
- 如果 \(f[i]\) 能够转移到的所有状态中有\(1\)个必败态\(,f[i]\)为必胜态
- 如果 \(f[i]\) 能转移到的所有状态中全是必胜态\(,f[i]\)为必败态
之所以会有以上两句结论,是因为\(f[i]\)定义中的"先手"并不是固定的,如现在\(f[1]\)的先手是\(Alice\),则转以后的"先手"就成了\(Bob\),因此此处的"先手"应该是"当前的操作者".
现在有一个游戏\(G2\),有\(N\)个石子,每次可以拿走\(3,4\)或\(5\)个石子.\(Alice\)先手.问\(Alice\)必胜还是必败
- 设\(f[i]\)为还剩\(i\)个石子时先手必胜还是必败.
- \(f[i]\) 可以转移到\(f[i-3],f[i-4],f[i-5]\)
- 如果\(f[i-3],f[i-4],f[i-5]\)中有一个为必败态,则\(f[i]\)为必胜态
- 如果\(f[i-3],f[i-4],f[i-5]\)全为必胜态,则\(f[i]\)为必败态
打表找规律:当 \(i\ MOD \ 8=0,1,2\) 时必胜,否则必败.
\(2\)档
两堆石头, \(N_1,N_2\) , 每次从其中某一堆取走任意多的石子,先手必胜/必败 \(?\)
- \(f[i][j]\) 表示两堆石子分别有\(i,j\)个时先手必胜还是必败.
- $ f[i][j]\rightarrow f[x][j]/f[i][y]\ \ ,\ \ $
\(3\)档
现在有一个游戏\(G\)由\(k\)个小游戏组成. $ G=(g_1,g_2.g_3,...,g_k)\ ,\ $第 \(i\) 个游戏是有$ N_i$ 堆石子的取石子游戏.
即\(Alice\)和\(Bob\)进行石子游戏,一共有\(k\)堆石子,第\(i\)堆石子有\(N_i\)个石子.\(Alice\)先手,轮流取石子,每次可以从某一堆石子中取出任意多的石子.问\(Alice\)必胜还是必败
解决这个问题需要引入一个数学概念:
\(SG\) 函数. \(sg[i]\) 为当石头还剩 \(i\) 个的时候的 \(SG\) 值
- 对于一个必败态,其SG值为\(0 \Longrightarrow sg[0]=0\) , 若\(SG\)值不为\(0\),则为必胜态.
- \(sg[i]=\ mex \{sg[i-1],sg[i-2],sg[i-3],...,sg[0]\}\)
- \(mex\ \ S\) : 返回最小的一个自然数\(n\),使\(n\not \in \ S\)
- \(\Longrightarrow \ sg[i]=i\)\(\ \ \ \ \ (*)\)
\(SG\) 定理 \(\ \ SG[G]=sg[g_1]\ \ XOR\ \ sg[g_2]\ \ XOR\ \ sg[g_3]\ \ XOR...XOR\ \ sg[g_k]\)
由\(SG\)定理和上面的结论\((*)\)可知 \(:\ \ SG[G]=n_1\ \ XOR\ n_2\ XOR\ n_3\ XOR\ ...\ XOR\ \ n_k\)
因此,此题的答案即为各堆石子的个数的异或
#include <cstdio>
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,sg,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&sg);
ans ^= sg;
}
if(ans) printf("Yes\n");
else printf("No\n");
}
return 0;
}