取石子游戏之尼姆博弈

尼姆博弈:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜

这种情况与二进制有着很大的关系,我们用(a,b,c)来表示某种局势,那么(0,0,0)必然为奇异局势,最后一个面对这个局势的必败。(0,n,n)也是种奇异局势。因为如果对手在其中一堆取m个石子(m<=n),那么你也可以在另外一堆中取m个,他取几个你就取几个,到最后有一堆变为0的时候,你再取完另一堆胜利。

直接说结论吧:

对于任意的奇异局势(a,b,c),都有a^b^c=0。(^为异或运算)

对于任意的非奇异局势(a,b,c),假设a<b<c,将它变为奇异局势的方法是:将c变成a^b。

原理:因为a^a=0,所以a^b^c=a^b^(a^b)=(a^a)^(b^b)=0,所以只需将c-(a^b)即可。

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

例2:我们来实际进行一盘比赛看看:
        甲:(7,8,9)->(1,8,9)奇异局势
        乙:(1,8,9)->(1,8,4)
        甲:(1,8,4)->(1,5,4)奇异局势
        乙:(1,5,4)->(1,4,4)
        甲:(1,4,4)->(0,4,4)奇异局势
        乙:(0,4,4)->(0,4,2)
        甲:(0.4,2)->(0,2,2)奇异局势
        乙:(0,2,2)->(0,2,1)
        甲:(0,2,1)->(0,1,1)奇异局势
        乙:(0,1,1)->(0,1,0)
        甲:(0,1,0)->(0,0,0)奇异局势
        甲胜。

性质1:对于某个局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。

分析:令res=a1^a2^......an,设res的最高位为pos(那pos的数肯定为1啦),那么一定存在某个ai,它的二进制在pos位上也是1,(否则k的最高位那个1是怎么得到的)。异或res后这位变为0,这时ai^res<ai一定成立。则我们可以将ai改变成ai'=ai^res,此时a1^a2^...^ai'^...^an=a1^a2^...^an^res=0。

性质2:对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后仍满足a1^a2^...^ai'^...^an=0。

例题1:POJ 2975,题目意思是给你一组局势,问有多少中必胜的策略。

分析:求出res=a[0]^a[1]^a[2].....a[n-1],由于每次只能改变一堆石子的数量,由性质1,可知如果res!=0,那么一定存在某个合法的操作,使得res=0,只要满足a[i]*res<=a[i]。

#include<iostream>
using namespace std;
const int MAX=1010;
int a[MAX]; 
int main()
{   int n,res;
    while(cin>>n,n)
    {   int count=0;
        for(int i=0;i<n;i++)
            cin>>a[i];    
        res=a[0];
        for(int i=1;i<n;i++)
            res^=a[i];
        if(res==0) cout<<0<<endl;
        else
        {   for(int i=0;i<n;i++)
               if((a[i]^res)<a[i])  count++; //要注意^和<=的优先级,必须加括号 
            cout<<count<<endl;
        } 
    }
    return 0;
}

SG函数:定义mex运算为最小不属于这个集合的非负整数。mex{0,1,2,4}=3,mex{}=0。

对于一个给定的有向无环图,我们定义关于图的每个顶点的SG函数g如下:g(x)=mex{g(y) | y是x的后继}

SG函数的性质:由于是一个有向无环图,那么对于所有的末端位置,由于没有后继,所以SG=0。另外对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0(由于集合中的元素是不可以重复的,所以后面的g(y)!=0)。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0(这个时候最小的非负整数一定是0)。

由上可知:顶点x所代表的位置是必败点条件是:g(x)=0

看到一个经典的话额,就贴在这里吧:

有些事,明知是错的,也要去坚持,因为不甘心;有些人,明知是爱的,也要去放弃,因为没结局;有时候,明知没路了,却还在前行,因为习惯了。
 
 
 
 
POJ 2488~八嘎
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int MAX=10;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int visit[MAX][MAX];
int n,m,dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1};
struct Point
{   int x,y;
};
Point Start;
vector<Point> V;
bool Inside(Point P)
{   return P.x>=1&&P.x<=m&&P.y>=1&&P.y<=n;
}
void DFS(Point P)
{   Point s;
    for(int i=0;i<8;i++)
    {   s.x=P.x+dx[i];
        s.y=P.y+dy[i];
        if(Inside(s)&&!visit[s.x][s.y]) 
        {   visit[s.x][s.y]=1;
            V.push_back(s);
            DFS(s);
        }
    }

int main()
{   int Case,num=1;
    cin>>Case;
    while(Case--)
    {   cin>>n>>m;
        CLR(visit,0);
        cout<<"Scenario #"<<num++<<":"<<endl;
       
        Start.x=1,Start.y=1;
        visit[1][1]=1;
        V.push_back(Start);
        DFS(Start);
        if(V.size()==n*m)
        {   for(vector<Point>::size_type i=0;i<V.size();i++)
                cout<<char(V[i].x+'A'-1)<<V[i].y;
            cout<<endl;
        }
        else cout<<"impossible"<<endl;
       
        V.clear();   
        if(Case) cout<<endl;
    } 
    return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

你可能感兴趣的:(取石子游戏之尼姆博弈)