51nod 1069 Nim游戏 + BZOJ 1022: [SHOI2008]小约翰的游戏John(Nim游戏和Anti-Nim游戏)
首先,51nod的那道题就是最简单的尼姆博弈问题。
尼姆博弈主要就是判断奇异局势,现在我们就假设有三个石子堆,最简单的(0,n,n)就是一个奇异局势,因为无论先手怎么拿,后手总是可以在另一堆里拿走相同的石子数。
再看另外一个奇异局势(1,2,3):
①如果先手拿第一个石子堆,那么后手可以形成(0,2,2)的局势,先手必败。
②如果先手拿第二个石子堆的1个石子,那么后手可以形成(1,1,0)的局势,先手必败。
③如果先手拿第二个石子堆的2个石子,那么后手可以形成(1,0,1)的局势,先手必败。
后面的同理分析即可。
现在我们需要考虑的是如何判断一个局势是否是奇异局势?
奇异局势的判断就是所有堆的值异或起来,如果最后等于0就是奇异局势,如果不是则不是奇异局势(异或的原理就是对于二进制的每一位进行运算,如果某一位最后为0,那么就说明该位上有偶数次1出现,偶数次说明什么呢?说明先手在某堆石子操作后,后手总能在另一堆石子里去做相对应的操作)。
那么如果先手面对的是非奇异局势,也只需要一步就可以变成奇异局势,将所有堆的值异或起来(除去最大堆),再用最大堆-该异或值,就是所拿石子数。
1 #include<cstdio> 2 using namespace std; 3 4 int n; 5 6 int main() 7 { 8 while(~scanf("%d",&n)) 9 { 10 int ans=0; 11 for(int i=0;i<n;i++) 12 { 13 int x; scanf("%d",&x); 14 ans^=x; 15 } 16 if(ans) puts("A"); 17 else puts("B"); 18 } 19 return 0; 20 }
接下来介绍一下anti-nim游戏,它的话就是取到最后一个石子输。
对于这种题目,它有一个专门的SJ定理:(具体的话就参见论文吧)
对于一个Anti-Nim游戏,只要有以下两条条件之一,先手必胜:
1.游戏的总SG函数为0且任意子游戏的SG函数不超过1;
2.游戏的总SG函数不为0且至少存在一个子游戏的SG函数超过1。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<stack> 7 #include<queue> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pll; 14 const int INF = 0x3f3f3f3f; 15 const int maxn = 50 + 5; 16 17 int n; 18 19 int main() 20 { 21 //freopen("in.txt","r",stdin); 22 int T; 23 scanf("%d",&T); 24 while(T--) 25 { 26 int sum=0; 27 scanf("%d",&n); 28 bool flag=false; 29 for(int i=1;i<=n;i++) 30 { 31 int x; scanf("%d",&x); 32 sum^=x; 33 if(x>1) flag=true; 34 } 35 if((!sum && !flag) || (sum && flag)) puts("John"); 36 else puts("Brother"); 37 } 38 return 0; 39 }