尼姆博奕 (Nimm Game) 异或运算;

来源:互联网 发布:达内软件科技 编辑:程序博客网 时间:2024/06/06 18:23

讲到博弈,不得不讲异或运算, 我真服了 大神了, 怎么会将博弈和二进制联系到一起,  大写的服!

异或运算 ^    原理就是  二进制形式,对于相应的每一位  相同 为0 不同为1 ;

简单理解就是不进位加法,如1+1=0,,0+0=0,1+0=1。

 a^(b^c)=(a^b)^c  这是结合律;

a^a=0;  0^x=x;  1^x=!x=1-x;

异或 可以实现两个 值的 交换而又不 借用第三者,  这在大量数据是 非常有效

int x=3,y=4;x=x^y;y=x^y;x=x^y;printf("%d  %d \n",x,y);

说回到博弈,  一般规定,谁最后拿 即为胜利, 有时也规定谁最后拿输掉,  

这个 时候 异或就非常有效,  我们这样来考虑,   将 物品分为N 堆, 每一堆都有至少一个物品,而要求一般是,最少拿一个,不能不拿, 上不封顶,  这就有一个先手与后手的问题,  先手怎样做才能保证一直赢,或者后手怎样做才能不输, 这就有策略的味道,网上的代码 有好多, 看了半天 晕啊晕,

首先 讲到一个 奇异局势(a^b^c=0),  

这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,...,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。这是网上威佐夫博弈(类似于尼姆博弈)的解释 看明白了么?  

不明白的话,  这样解释一下,  奇异局势 就是 上一个人拿完后 轮到你时,当前你要面对的 局势,   而在尼姆博弈中无论谁面对奇异局势都是必败,, 我们可知,  (0,0,0)是奇异局势, ,(0.n,n)也是,此时 无论你怎样做都是无用功,例如(0,3,3) (如果你想去个3或者2(其实都一样), 对方只要在另一堆中去到只剩下1  那么接下来,你必须首先得先干掉1,此时仍有棋子在上面,对方全部取走,你就必输)


由此可知:

1. 无论谁面对奇异局势 则必输,

2. 奇异局势,取若干个,变成非奇异, 非奇异也能取若干个到奇异局势, 

3.奇异局势 结果 a^b^c^...=0  结果必为0


如何 从非奇异到奇异  a^b^c=0,  把c 变成 a^b  这是可以的 因为a^a =0 ;

然后 c-a^b 就是取走的个数


说了这么多,  没有代码怎么行;

/*谁取最后一个 先胜, A 先手 */#include<stdio.h>int main(){int x,n,m,i,sum;scanf("%d",&n);sum=0;for(i=0;i<n;i++){scanf("%d",&x);sum^=x;}if(sum)printf("A胜\n");elseprintf("B胜\n"); //b 面对奇异局势   return 0;}


来看几道题

hdu 1850Being a Good Boy in Spring Festival

Problem Description
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”
 

Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。
 

Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。
 

Sample Input
35 7 90
 

Sample Output
1
 

输出可行方案数#include<stdio.h>#include<string.h>#include<iostream>using namespace std;int a[500];int main(){    int n;    while(cin>>n&&n)    {        memset(a,0,sizeof(a));        int sum=0,res=0;        for(int i=0;i<n;i++)        {            cin>>a[i];            sum^=a[i];                    }        for(int i=0;i<n;i++)        {            if(a[i]>(sum^a[i]))//就是说c 可以用a^b 来替换的话 前提要比a^b 大             {                res++;            }        }        cout<<res<<endl;    }    return 0;} 
hdu 1907  John

Problem Description
Little John is playing very funny game with his younger brother. There is one big box filled with M&Ms of different colors. At first John has to eat several M&Ms of the same color. Then his opponent has to make a turn. And so on. Please note that each player has to eat at least one M&M during his turn. If John (or his brother) will eat the last M&M from the box he will be considered as a looser and he will have to buy a new candy box.

Both of players are using optimal game strategy. John starts first always. You will be given information about M&Ms and your task is to determine a winner of such a beautiful game.

 

Input
The first line of input will contain a single integer T – the number of test cases. Next T pairs of lines will describe tests in a following format. The first line of each test will contain an integer N – the amount of different M&M colors in a box. Next line will contain N integers Ai, separated by spaces – amount of M&Ms of i-th color.

Constraints:
1 <= T <= 474,
1 <= N <= 47,
1 <= Ai <= 4747

 

Output
Output T lines each of them containing information about game winner. Print “John” if John will win the game or “Brother” in other case.

 

Sample Input
233 5 111
 
#include <iostream>#include <cstring>#include <stdio.h>using namespace std;int main(){    int t,n,m,res;    int a[5000];    cin>>t;    while(t--)    {        cin>>n;        m=res=0;        memset(a,0,sizeof(a));        for(int i=0;i<n;i++)        {            cin>>a[i];            m^=a[i];            if(m>1)                res++;        }        if((m&&res)||(m==0&&res==0))//john 先手, 当他m 不等于0 (非奇异局势) 并且 res的个数大于0 胜利            printf("John\n");//或者 0 并且res=0;        else            printf("Brother\n");    }    return 0;}



hdu 2509Be the Winner

Problem Description
Let's consider m apples divided into n groups. Each group contains no more than 100 apples, arranged in a line. You can take any number of consecutive apples at one time.
For example "@@@" can be turned into "@@" or "@" or "@ @"(two piles). two people get apples one after another and the one who takes the last is 
the loser. Fra wants to know in which situations he can win by playing strategies (that is, no matter what action the rival takes, fra will win).
 

Input
You will be given several cases. Each test case begins with a single number n (1 <= n <= 100), followed by a line with n numbers, the number of apples in each pile. There is a blank line between cases.
 

Output
If a winning strategies can be found, print a single line with "Yes", otherwise print "No".
 

Sample Input
22 213
 

Sample Output
NoYes

123

#include <iostream>#include <cstring>#include <algorithm>#include <stdio.h>using namespace std;int a[500];int main(){    int n,res,m,sum;    int  full;    while(cin>>n)    {        res=sum=0;        full=0;        for(int i=0;i<n;i++)        {            cin>>a[i];            sum^=a[i];            if(a[i]>1)                full=1;        }        if(full)// 非孤单堆        {            if(sum)                printf("Yes\n");            else                printf("No\n");        }        else//孤单堆   需考虑        {            if(!sum)            {                printf("Yes\n");            }            else            {                if((n&1))                    printf("No\n");                else                    printf("Yes\n");            }        }    }    return 0;}




0 0
原创粉丝点击