题意
题解
博弈问题,考虑 g r u n d y grundy grundy 数,但每一轮除了选择一堆石子移除至少一颗石子外,其余石子还可以任意转移到其他石子数非零的堆中,状态转移数过多。需要寻找其他方法判断胜负态。
在这类游戏当中,做出对称的状态再完全模仿对手的策略常常是有效的。
取走最后一堆石子的一方获胜,那么此时必胜态为仅剩一堆石子的状态。考虑
N
i
m
Nim
Nim 的异或运算构造必胜态,可以发现甚至无法通过样例。考虑石子堆的奇偶性,当石子堆中石子出现的个数为偶数,那么后手的一方可以模仿先手的策略使状态回到偶数情况。有以下结论成立:
相
同
石
子
数
的
堆
的
个
数
都
为
偶
数
→
必
胜
态
相同石子数的堆的个数都为偶数\rightarrow 必胜态
相同石子数的堆的个数都为偶数→必胜态
相
同
石
子
数
的
堆
的
个
数
存
在
奇
数
→
必
败
态
相同石子数的堆的个数存在奇数\rightarrow 必败态
相同石子数的堆的个数存在奇数→必败态 简略地证明一下。首先一旦从偶数状态的某一堆移除至少一颗石子,则一定存在相同石子的堆个数为奇数。假设从石子数为
x
i
x_i
xi 的一堆中移除了
a
a
a 个石子,分配了
b
b
b 个,分配给另一个
x
i
x_i
xi 了
e
e
e 个;此时原本石子数为
x
i
x_i
xi 的一对堆被拆开,变为
(
x
i
+
e
,
x
i
−
a
−
b
)
(x_i+e,x_i-a-b)
(xi+e,xi−a−b),无法通过分配
b
b
b 再次配成一对;反证法,在不产生新的奇数堆的情况下,假设能让
x
i
,
x
i
−
a
−
b
x_i,x_i-a-b
xi,xi−a−b 都能与另一对
(
x
j
,
x
j
)
(x_j,x_j)
(xj,xj) 配成新的两对偶数堆,设分配的
b
b
b 个石子中有
c
c
c 个分配给某一个
x
j
x_j
xj,有
d
d
d 偶数个分配给其余的偶数堆,那么有
x
i
+
e
=
x
j
+
b
−
c
−
d
−
e
,
x
i
−
a
−
b
=
x
j
+
c
x_i+e=x_j+b-c-d-e,x_i-a-b=x_j+c
xi+e=xj+b−c−d−e,xi−a−b=xj+c,消去
x
i
,
x
j
x_i,x_j
xi,xj,得到
a
+
2
c
+
d
+
2
e
=
0
a+2c+d+2e=0
a+2c+d+2e=0,与
a
≥
1
a\geq 1
a≥1 矛盾。可以证实必败态只能转移到必胜态。
假设只存在个数为某个值 x i x_i xi 的石子堆为奇数堆,那么将其中一堆取完就可以将其转换为偶数态。若存在多个值的石子堆为奇数堆,对于每一个值只取一堆进行处理,此时剩余的堆为偶数状态;假设升序排序后要处理的堆石子值分别为 x 1 , x 2 … x n x_1,x_2\dots x_n x1,x2…xn,根据差分的思想,有 x 1 + ( x 2 − x 1 ) + ⋯ + ( x n − x n − 1 ) = x n x_1+(x_2-x_1)+\dots+(x_n-x_{n-1})=x_n x1+(x2−x1)+⋯+(xn−xn−1)=xn;若 n n n 为奇数,考虑到对于任意 i ∈ [ 1 , n ) i\in[1,n) i∈[1,n) 有 x i + 1 − x i ≥ 1 x_{i+1}-x_i\geq 1 xi+1−xi≥1,此时有 x n > ( x 2 − x 1 ) + ( x 4 − x 3 ) + ⋯ + ( x n − 1 − x n − 2 ) x_n>(x_2-x_1)+(x_4-x_3)+\dots+(x_{n-1}-x_{n-2}) xn>(x2−x1)+(x4−x3)+⋯+(xn−1−xn−2),按顺序将 x n x_n xn 中的石子分配给括号中每一对中的较小值,其余石子都移除,即可转换为偶数状态;若 n n n 为偶数,此时有 x n > x 1 + ( x 3 − x 2 ) + ( x 5 − x 4 ) + ⋯ + ( x n − 1 − x n − 2 ) x_n>x_1+(x_3-x_2)+(x_5-x_4)+\dots+(x_{n-1}-x_{n-2}) xn>x1+(x3−x2)+(x5−x4)+⋯+(xn−1−xn−2),按顺序将 x n x_n xn 中的石子分配给括号中每一对中的较小值,然后移除至少一颗石子使 x n x_n xn 变为 x 1 x_1 x1,即可转换为偶数状态。可以证实必胜态总是能转移到某个必败态。
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxp 100
int main()
{
int n, cnt[maxp + 1];
while (~scanf("%d", &n) && n)
{
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; ++i)
{
int num;
scanf("%d", &num);
++cnt[num];
}
int res = 0;
for (int i = 1; i <= maxp; ++i)
{
if (cnt[i] & 1)
{
res = 1;
break;
}
}
printf("%d\n", res);
}
}