892. 台阶-Nim游戏
现在,有一个 n
级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i≥1
)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n
。
第二行包含 n
个整数,其中第 i 个整数表示第 i 级台阶上的石子数 ai
。
输出格式
如果先手方必胜,则输出 Yes
。
否则,输出 No
。
数据范围
1≤n≤105
,
1≤ai≤109
输入样例:
3
2 1 3
输出样例:
Yes
Nim游戏的变种;
* 刚开始的时候没想出来,最开始的时候,知道应该要分为奇数阶和偶数阶来讨论,于是有了
* 下面的想法:
* 当偶数阶的异或和为0的时候,先手胜,反之先手败;(现在想想这种猜测是错误的)
* 当奇数阶的异或和为0的时候,先手败,反之先手胜;
*
* 于是两种情况不知道怎么判断,emm,然后看了别人的题解,看了一眼结论:就是
* 当奇数阶的异或和为0的时候,先手败,反之先手胜;这个结论。
* 随后接着来思考,想出了其中的道道。
*
* 给出当奇数阶的异或和为0的时候,先手败,反之先手胜的证明;
* 证明:
* 通过Nim游戏我们已经证明了,当奇数阶的异或和为0的时候,取一个正整数之后,
* 异或和一定不为0,那么对手一定能取一个数使之异或和变为0;这样进行n轮循环
* 之后,一定使先手没有石子可拿,所以先手败北。反之,当奇数阶的异或和不为0的时候,
* 必然是先手胜。
* 然后,我们来唠唠对手取双数阶上的石子的时候,怎么办。当奇数阶的异或和不为0的时候,
* 我使取一个数使之异或和变为0,轮到对手,对手取偶数阶上的石子时,对手取多少到石子
* 到下一阶的台阶上,我们就把他放到单数阶上的石子数原数放到下一阶去,这就使得奇数
* 阶的异或和还是为0(因为每一个单数阶上的石子都没变)。
*
* 核心:当异或和不为0,先手总是取一个数使必败状态留给对方。
* 当异或和为0(先手处于必败状态),先手不管怎么操作,后手都会把必败状态还给先手
* (别想给我下套);
/**
* Nim游戏的变种;
* 刚开始的时候没想出来,最开始的时候,知道应该要分为奇数阶和偶数阶来讨论,于是有了
* 下面的想法:
* 当偶数阶的异或和为0的时候,先手胜,反之先手败;(现在想想这种猜测是错误的)
* 当奇数阶的异或和为0的时候,先手败,反之先手胜;
*
* 于是两种情况不知道怎么判断,emm,然后看了别人的题解,看了一眼结论:就是
* 当奇数阶的异或和为0的时候,先手败,反之先手胜;这个结论。
* 随后接着来思考,想出了其中的道道。
*
* 给出当奇数阶的异或和为0的时候,先手败,反之先手胜的证明;
* 证明:
* 通过Nim游戏我们已经证明了,当奇数阶的异或和为0的时候,取一个正整数之后,
* 异或和一定不为0,那么对手一定能取一个数使之异或和变为0;这样进行n轮循环
* 之后,一定使先手没有石子可拿,所以先手败北。反之,当奇数阶的异或和不为0的时候,
* 必然是先手胜。
* 然后,我们来唠唠对手取双数阶上的石子的时候,怎么办。当奇数阶的异或和不为0的时候,
* 我使取一个数使之异或和变为0,轮到对手,对手取偶数阶上的石子时,对手取多少到石子
* 到下一阶的台阶上,我们就把他放到单数阶上的石子数原数放到下一阶去,这就使得奇数
* 阶的异或和还是为0(因为每一个单数阶上的石子都没变)。
*
* 核心:当异或和不为0,先手总是取一个数使必败状态留给对方。
* 当异或和为0(先手处于必败状态),先手不管怎么操作,后手都会把必败状态还给先手
* (别想给我下套);
*/
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int res=0;
for(int i=1;i<=n;++i)
{
int val;
cin >> val;
if(i%2)
res^=val;
}
if(res)
puts("Yes");
else
puts("No");
return 0;
}
894. 拆分-Nim游戏
给定 n
堆石子,两位玩家轮流操作,每次操作可以取走其中的一堆石子,然后放入两堆规模更小的石子(新堆规模可以为 0
,且两个新堆的石子总数可以大于取走的那堆石子数),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n
。
第二行包含 n
个整数,其中第 i 个整数表示第 i 堆石子的数量 ai
。
输出格式
如果先手方必胜,则输出 Yes
。
否则,输出 No
。
数据范围
1≤n,ai≤100
输入样例:
2
2 3
输出样例:
Yes
想想Mex函数的定义,想想SG函数的定义:
#include <iostream>
#include <cstring>
#include <set>
using namespace std;
const int maxn = 110;
int h[maxn];
int SG(int x)
{
if(h[x] != -1)
return h[x];
set<int> st;
for(int i=0;i<x;++i)
for(int j=0;j<=i;++j)
st.insert(SG(i)^SG(j));
for(int i=0;;++i)
if(!st.count(i))
return h[x]=i;
}
int main()
{
memset(h, -1, sizeof h);
int n;
cin >> n;
int res=0;
while (n -- )
{
int x;
cin >> x;
res^= SG(x);
}
if(res)
puts("Yes");
else
puts("No");
return 0;
}