本文不讲述复杂的推导过程与证明过程,直接给出结论,方便记忆。
尼姆游戏:
有n堆若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
稍叫说明,(0,0,0,…,0)为必败状态,不论谁面对这种状态都是必败。
给出下面两个规则:
1.一个状态是必败状态当且仅当它所有的后继都是必胜状态。
2.一个状态是必胜状态当且仅当它至少有一个后继状态是必败状态。
(相关分析可以看刘汝佳的蓝书P133)
然后Bouton根据上面的两个规则,给出下面的定理:
(x1,x2,x3)为必败状态当且仅当x1 xor x2 xor x3 =0 称为Nim 和
(小声bb一句,能结合异或也tql。。。
举个小例子,(13,12,8) 先手面对这个状态求nim和,异或起来不是0,所以不是必败状态,所以先手胜。但是他要怎么操作呢,很简单,就是让对手面对必败状态,他只需要取走第三堆里的7个物品,留下一个,变成(4,12,8)
这样nim和就是0了。(稍微想想,a XOR b XOR (a XOR b)=(a XOR a) XOR (b XOR b) = 0 XOR 0 = 0 要将c 变为a XOR b,只要对 c进行 c-(a XOR b)这样的运算即可 )
然后放上两个例题
链接:https://ac.nowcoder.com/acm/contest/549/I
来源:牛客网
小A也听说了取石子这个游戏,也决定和小B一起来玩这个游戏。总共有n堆石子,双方轮流取石子,每次都可以从任意一堆中取走任意数量的石子,但是不可以不取。规定谁先取完所有的石子就获胜。但是小A实在是太想赢了,所以在游戏开始之前,小A有一次机会,可以趁小B不注意的时候选择其中一堆石子拿走其中的k个,当然小A也可以选择不拿石子。小A先手。双方都会选择最优的策略,请问在这样的情况下小A有没有必胜的策略,如果有输出YES,否则就输出NO。
链接:https://ac.nowcoder.com/acm/contest/549/I
来源:牛客网
其实就是尼姆游戏的一个加强吧,多了一个变数k
出题人的巧妙方法就是 如果初始值的nim和为0,并且他不能取任何石子,那么他就面临必败态,其余状态都是必胜
当然了,你也可以先求出nim和,然后枚举取某个点k个物品的nim和,如果都还是为0,那么还是不能赢
上第一种思路的AC代码吧
#include <bits/stdc++.h>
using namespace std;
const int MAX = 5e6 + 6;
int a[MAX];
int main(){
int N,k;
cin >> N >> k;
int nim = 0;
int flag = 0;
for (int i = 0; i < N;i++){
cin >> a[i];
nim ^= a[i];
if(a[i]>=k)
flag = 1;
}
if(nim==0&&(flag==0||k==0)){
cout << "NO" << endl;
}
else {
cout << "YES" << endl;
}
return 0;
}
再上个裸题吧,就简单的nim和,POJ2234
直接上代码了
#include <iostream>
using namespace std;
int main (){
int n;
int x;
while(cin>>n){
int nim = 0;
for (int i = 0; i < n;i++){
cin >> x;
nim ^= x;
}
if(nim==0){
cout << "No" << endl;
}
else {
cout << "Yes" << endl;
}
}
return 0;
}