您的位置:首页 > 其它

51nod 1069 Nim游戏 + BZOJ 1022: [SHOI2008]小约翰的游戏John(Nim游戏和Anti-Nim游戏)

2017-10-11 11:22 399 查看
首先,51nod的那道题就是最简单的尼姆博弈问题。

尼姆博弈主要就是判断奇异局势,现在我们就假设有三个石子堆,最简单的(0,n,n)就是一个奇异局势,因为无论先手怎么拿,后手总是可以在另一堆里拿走相同的石子数。

再看另外一个奇异局势(1,2,3):

①如果先手拿第一个石子堆,那么后手可以形成(0,2,2)的局势,先手必败。

②如果先手拿第二个石子堆的1个石子,那么后手可以形成(1,1,0)的局势,先手必败。

③如果先手拿第二个石子堆的2个石子,那么后手可以形成(1,0,1)的局势,先手必败。

后面的同理分析即可。

现在我们需要考虑的是如何判断一个局势是否是奇异局势?

奇异局势的判断就是所有堆的值异或起来,如果最后等于0就是奇异局势,如果不是则不是奇异局势(异或的原理就是对于二进制的每一位进行运算,如果某一位最后为0,那么就说明该位上有偶数次1出现,偶数次说明什么呢?说明先手在某堆石子操作后,后手总能在另一堆石子里去做相对应的操作)。

那么如果先手面对的是非奇异局势,也只需要一步就可以变成奇异局势,将所有堆的值异或起来(除去最大堆),再用最大堆-该异或值,就是所拿石子数。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn = 50 + 5;

int n;

int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
int sum=0;
scanf("%d",&n);
bool flag=false;
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
sum^=x;
if(x>1)  flag=true;
}
if((!sum && !flag) || (sum && flag))  puts("John");
else puts("Brother");
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: