Nim博弈的原题:
问题1:今有若干堆火柴,两人依次从中拿取,规定每次只能从一堆中取若干根, 可将一堆全取走,但不可不取,最后取完者为胜,求必胜的方法。
定义:若所有火柴数异或为0,则该状态被称为利他态,用字母T表示;否则, 为利己态,用S表示。
注意:这篇博文是先定义s和t,再通过它们的性质推出结论。
[定理1]:对于任何一个S态,总能从一堆火柴中取出若干个使之成为T态。
证明:
若有n堆火柴,每堆火柴有A(i)根火柴数,那么既然现在处于S态, c = A(1) xor A(2) xor … xor A(n) > 0;
把c表示成二进制,记它的二进制数的最高位为第p位,则必然存在一个A(t),它二进制的第p位也是1。
(否则,若所有的A(i)的第p位都是0,这与c的第p位就也为0矛盾)。
那么我们把x = A(t) xor c,则得到x < A(t).这是因为既然A(t)的第p位与c的第p位同为1,那么x的第p位变为0,而高于p的位并没有改变。
所以x < A(t).而
A(1) xor A(2) xor … xor x xor … xor A(n)
= A(1) xor A(2) xor … xor A(t) xor c xor … xor A(n)
= A(1) xor A(2) xor… xor A(n) xor A(1) xor A(2) xor … xor A(n)
= 0
这就是说从A(t)堆中取出 A(t) - x 根火柴后状态就会从S态变为T态。证毕。
[定理2]:T态,取任何一堆的若干根,都将成为S态。
证明:用反证法试试。
若
c = A(1) xor A(2) xor … xor A(i) xor … xor A(n) = 0;
c' = A(1) xor A(2) xor … xor A(i') xor … xor A(n) = 0;
则有:
c xor c' = A(1) xor A(2) xor … xor A(i) xor … xor A(n) xor A(1) xor A(2) xor … xor A(i') xor … xor A(n) = A(i) xor A(i') =0
进而推出A(i) = A(i'),这与已知矛盾。所以命题得证。
[定理 3]:S态,只要方法正确,必赢。
最终胜利即由S态转变为T态,任何一个S态,只要把它变为T态,(由定理1,可以把它变成T态。)对方只能把T态转变为S态(定理2)。这样,所有S态向T态的转变都可以有己方控制,对方只能被动地实现由T态转变为S态。因为全零属于T态,故S态必赢。(不能单单从对称拿取来考虑这个问题。例如a=b xor c,然后求sg这种情况。因为证不出来。。定理1只是说存在这种情况,并没有说对称拿取。)
[定理4]:T态,只要对方法正确,必败。
由定理3易得。
反尼姆博弈
问题2:今有若干堆火柴,两人依次从中拿取,规定每次只能从一堆中取若干根, 可将一堆全取走,但不可不取,最后取完者为负,求必胜的方法。
定义:若一堆中仅有1根火柴,则被称为孤单堆。若大于1根,则称为充裕堆
定义:T态中,若充裕堆的堆数大于等于2,则称为完全利他态,用T2表示;若充裕堆的堆数等于0,则称为部分利他态,用T0表示。
孤单堆的根数异或只会影响二进制的最后一位,但充裕堆会影响高位(非最后一位)。一个充裕堆,高位必有一位不为0,则所有根数异或不为0。故不会是T态。
同上S表示异或和>0,T表示异或和为0
[定理5]:S0态,即仅有奇数个孤单堆,必败。T0态必胜。
S0表示异或和>0且无充裕堆(只有孤单堆) => 因为孤单堆只会影响二进制的最后一位,
所以当异或和>0时说明仅有奇数个孤单堆 于是必败(比如有3个孤单堆,先手肯定把最后一个给拿走,先手必输。)
同理,T0表示异或和为0且无充裕堆 => 于是一定仅有偶数个孤单堆。(后手一定把最后一个给拿走,先手必胜。)
[定理6]:S1态,只要方法正确,必胜。
S1态表示异或和>0,且有1个充裕堆。注:不存在T1态,因为有一个充裕堆的时候,其异或值一定不为0(充裕堆会影响高位)
有两个操作
1.当孤单堆为奇数的时候,先手只需要将唯一的充裕堆给全拿走。后手就进入了S0态。先手必胜!
2.当孤单堆为偶数的时候,先手只需要将唯一的充裕堆拿的只剩下一个。后手又进入里S0态。先手必胜!
[定理7]:S2态不可转一次变为T0态。
充裕堆数量不可能一次从2个变成1个
[定理8]:S2态可一次转变为T2态。
由定理1可证明S态一定可以通过某种操作变成T态。
又由定理7可以得知S2态不可能变成T0态,且不存在T1态,于是只能变成T2态。
[定理9]:T2态,只能转变为S2态或S1态。
由定理7可以得知,2个充裕堆不可能一次变成0个
又由定理二可以得知T态通过任意的变化都要变成S态
于是T2态只能变成S2态或者S1态
[定理10]:S2态,只要方法正确,必胜.
方法如下:
1) S2态,就把它变为T2态。(由定理8)
2) 对方只能T2转变成S2态或S1态(定理9)
若转变为S2, 转向1)
若转变为S1, 这己必胜。(定理6)
[定理11]:T2态必输。
证明同定理10
先手只能将T2变成S1或S2
1)S1态 后手胜!
2)S2态 后手将S2变成T2,先手重新选择。
所以:
必胜态:T0,S1,S2
必败态:S0,T2
比较2题
T0(第一题做法)。哪一方控制了S1态,他即可以有办法使自己得到最后一根(转变为 T0),
也可以使对方得到最后一根(转变为S0)。
所以,抢夺S1是制胜的关键!
为此,始终把T2态让给对方,将使对方处于被动状态,他早晚将把状态变为S1.
例题
HDU1907
反Nim博弈原题
#include<bits/stdc++.h>
using namespace std;
int main()
{
int caset;scanf("%d",&caset);
while(caset--) {
int cnt = 0,ans = 0;
int n;scanf("%d",&n);
for(int i=0,x;i<n;i++) {
scanf("%d",&x);
if(x > 1) cnt++;
ans ^= x;
}
if(ans) { /// S
if(cnt == 0) printf("Brother\n"); /// S0
else printf("John\n"); /// S1,S2
}
else { /// T
if(cnt == 0) printf("John\n"); /// T0
else printf("Brother\n"); /// T2
}
}
return 0;
}
HDU-2509
反尼姆博弈
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
while(~scanf("%d",&n)) {
int cnt = 0,ans = 0;
for(int i=0,x;i<n;i++) {
scanf("%d",&x);
if(x > 1) cnt++;
ans ^= x;
}
if(ans) { /// S
if(cnt == 0) printf("No\n"); /// S0
else printf("Yes\n"); /// S1,S2
}
else { /// T
if(cnt == 0) printf("Yes\n");/// T0
else printf("No\n"); /// T2
}
}
return 0;
}