尼姆博弈

处理何种问题:有n堆物品,每堆物品数为 arr[i] ,双方轮流从中取物品,每次只能从一堆物品中取任意件物品,最少取一件,取到最后一件物品的人获胜,求谁最后获胜。

 

性能:因为是有公式,所以时间复杂度为O(n)。

 

原理:结论:把每堆物品数全部都异或起来,如果得到的值为0,那么先手必败,否则先手必胜。

在这里补充一个知识点,异或:相同为0,不同为1,同一些数异或的顺序不会对最终结果有何影响。

 

以三堆物品为例:

我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。

重点:任何奇异局势(a,b,c)都有a^b^c =0。

如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a^b,即可,因为有如下的运算结果: a^b^(a^b)=(a^a)^(b^b)=0^0=0。要将c 变为a^b,只要从 c中减去 c-(a^b)即可。

 例1。(14,21,39),14^21=27,39-27=12,所以从39中拿走12个物体即可达到奇异局势(14,21,27)。

例2。(55,81,121),55^81=102,121-102=19,所以从121中拿走19个物品就形成了奇异局势(55,81,102)。

例3。(29,45,58),29^45=48,58-48=10,从58中拿走10个,变为(29,45,48)。

 

备注:记住结论:异或结果为0,先手必败,否则必胜。

 

输入样例解释

5//n堆

12 58 6 22 54//每堆石子个数

输出样例解释

First Win //先手胜

 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int main()
{
    int n,temp,ans;
    while(~scanf("%d",&n))
    {
        ans=0;// 任意一个数异或 0 ,都是该数
        for(int i=0;i<n;++i)
        {
            scanf("%d",&temp);
            ans=(temp^ans);//异或运算符优先度较低,注意加括号
        }
        if(ans)
            printf("First Win\n");
        else
            printf("First Lose\n");

    }
    return 0;
}

  

posted @ 2018-08-16 16:59  逃往火星的猫  阅读(192)  评论(0编辑  收藏  举报