891. Nim游戏
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n。
第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量。
输出格式
如果先手方必胜,则输出 Yes
。
否则,输出 No
。
数据范围
1≤n≤105,
1≤每堆石子数≤109
输入样例:
2
2 3
输出样例:
Yes
先手必胜态:可以走到某一个必败状态
先手必败态:无法走到一个必胜状态
定理:如果初始所有值的异或结果不为0,则先手必胜,反之必败
证明:
①败的最终状态000^0…=0
②当a[1] ^ a[2] ^ … ^ a[n] = x != 0 时
记x的二进制表示中最高位的1是第k位,则肯定存在一个a[i]的第k位为1(因为要使两个数异或结果为1,必然要有一个1)
可知a[i]^x < a[i](因为x的最高位的1在k,第k位都为1,因此异或后第k为0,比k位低的异或结果不管是怎么样的,最终结果肯定比a[i])
因此可以把a[i]变成a[i]^x ,即从a[i]中拿走a[i]-a[i]^x个
这样结果会变成a[1] ^ a[2] ^ … ^ a[i] ^ x ^ … ^ a[n] = 0
②当a[1] ^ a[2] ^ … ^ a[n]= 0 (式1)时
当从任意一堆中拿走任何数量的石子后,异或结果肯定不等于0
反证法证明:
若从任意一堆a[i]中拿走任何数量的石子后(a[i]变成a[t]),异或结果等于0,则a[1] ^ a[2] ^ … ^ a[t] ^ … a[n] = 0(式2)
则 式1^式2 = 0 -> a[t] ^ a[i] = 0 -> a[t] = a[i] , 这与a[i] > a[t]矛盾,因此假设不成立
因此若先手的状态为②,那么总存在一种取法,使得结果变成③,当③变成①的状态时,后手的人无法操作,先手胜利
反之同理。
所以证得:如果初始所有值的异或结果不为0,则先手必胜,反之必败
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
typedef long long LL;
int p[N];
int main()
{
int n;
cin >> n;
int res = 0;
while (n--)
{
int temp;
cin >> temp;
res ^= temp;
}
if (res)
cout << "Yes" << endl;
else
cout << "No" << endl;
}