三大基础博弈(必看 未看)(转)

(一)、acm博弈基础算法


Bash Game,Nim Game和Wythoff Game(即 巴什博奕、尼姆博弈、威佐夫博弈)








Bash    Game:   同余理论


Nim      Game:  异或理论


Wythoff Game:  黄金分割










(二)、三个博弈。


1、巴什博奕。




只有一堆n个物品,两个人轮流从这堆物品中取物,


 规定每次至少取一个,最多取m个。最后取光者得胜。






如果n=m+1,那么由于一次最多只能取m个,


所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。


只要保持给对手留下(m+1)的倍数,就能最后获胜.

[cpp] view plain copy
while(~scanf("%d%d",&n,&m))  
{  
    if(n%(m+1)==0)//必输   
        puts("lose");  
    else  
        puts("win");  
}  

巴什博奕练习题HDU2149

代码:


[cpp] view plain copy
//HDU2149巴什博奕  
#include<stdio.h>  
int main()  
{  
    int m,n;//土地价值m,每次最多取n个   
    while(~scanf("%d%d",&m,&n))  
    {  
        if(m%(n+1)==0)//必输   
            puts("none");  
        else  
        {//要给对手留下奇异局势(必输局势)   
            int flag=1;  
            for(int i=1;i<=n;i++)//枚举  
            {  
                int mk=m-i<0? 0:m-i;  
                if(mk%(n+1)==0)//对手面对奇异局势  
                {  
                    if(flag)  
                        printf("%d",i),flag=0;  
                    else  
                        printf(" %d",i);   
                }  
            }  
            puts("");  
        }  
    }  
    return 0;  
 }   




2、尼姆博弈


有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,


规定每次至少取一个,多者不限,最后取光者得胜。




尼姆博弈说的是三堆,但是他的方法却可以推广到任意堆。



尼姆博弈我只会用它的公式,至于原理,,搞不了






即,把所有堆的数量亦或起来,若为0,则先手必输






还要知道一个常识:a, b, a ^ b 三个数之间,两两亦或可得第三个数






分析:


存在一些奇异局势,会使得先手必输,如下面的情况(三堆时)


(0,0,0),(0,n,n)






只要面对这种局势,必输。




任何奇异局势(a,b,c)都有a(+)b(+)c =0。
获胜策略:所以从一个非奇异局势向一个奇异局势转换的方式可以是:


1)使 a = c(+)b
2)使 b = a(+)c
3)使 c = a(+)b
例如:(14,21,39),14(+)21=27,39-27=12,
所以从39中拿走12个物体即可达到奇异局势(14,21,27)。








尼姆博弈练习题:HDU1850






分析:该题在求得先手是否有必赢策略的基础上,若能赢,输出方案数。






假如每一堆都可以取,取了之后必胜,方案数最多最多等于m(堆数)


看上面的获胜策略,想要留给对手奇异局势,就必须在某一堆取出一些,
使剩下的局势成为奇异局势。
假如我取第c堆想赢,就要保证c的数量大于其他堆的异或结果,才够减
等于不行,等于的话相当于把第c堆取完,
那么其余堆的总亦或不为0,这就给了对手有必赢策略的机会
代码:
[cpp] view plain copy
#include<stdio.h>  
int main()  
{  
    int m;  
    int a[10000];  
    while(scanf("%d",&m),m)  
    {  
        int ans=0;  
        for(int i=0;i<m;i++)  
        {  
            scanf("%d",&a[i]);  
            ans=ans^a[i];  
        }  
        if(ans==0)  
            puts("0");//输  
        else  
        {  
            int count=0;  
            for(int i=0;i<m;i++)  
            {  
                if(a[i]-(ans^a[i])>0)//ans^a[i]得到的是除a[i]外所有堆的亦或结果   
                    count++;  
            }  
            printf("%d\n",count);  
        }  
    }  
    return 0;  
}  
3、威佐夫博弈
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,
规定每次至少取一个,多者不限,最后取光者得胜。
分析:
对于给出的两堆石子,记为a个和b个(a<=b),(a,b)。
存在一些特殊情况,当你面对下面一些情况时,必输。
(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)。
先看(0,0),若你目前面对这个局势,说明你的对手抢先一步赢了。
再看(1,2),不管你怎么取,你取完后,剩下只可能是(0,1)(0,2)(1,1),对手就会赢
上面的情况称为奇异局势(即 必输)。可以通过下面的公式得到这些奇异局势
a = [ k * eqa ]  ([ ] 代表取整数部分)
b = a + k
其中epa = (1+√5)/2= 1.618033 = (1+sqrt(5.0))/2.0 
可以用这个通项公式打表:
[cpp] view plain copy
#include<stdio.h>  
#include<math.h>  
const int N=1000;  
const double eqa=(1+sqrt(5.0))/2.0;  
int a[N],b[N];  
void makelose()  
{  
    for(int k=0;k<N;k++)  
    {  
        a[k]=(int)(k*eqa);  
        b[k]=a[k]+k;  
    }  
}  
也可以判断任意给出的(a,b)是不是奇异局势
[cpp] view plain copy
scanf("%d%d",&a,&b);   
if(a>b)  
{  
    a=a^b;  
    b=a^b;  
    a=a^b;  
}  
int k=b-a;  
if(a==(int)(k*eqa))  
    puts("0");//是奇异局势,必败  
else  
    puts("1");  
威佐夫博弈练习题 HDU1527
取石子游戏
AC代码:
[cpp] view plain copy
<span style="font-size:18px;">#include<stdio.h>  
#include<math.h>  
const double eqa=(1+sqrt(5.0))/2.0;  
int main()  
{  
    int a,b;  
    while(~scanf("%d%d",&a,&b))  
    {  
        if(a>b)  
        {  
            a=a^b;  
            b=a^b;  
            a=a^b;  
        }  
        int k=b-a;  
        if(a==(int)(k*eqa))  
            puts("0");//遇到奇异局势,必败  
        else  
            puts("1");  
    }  
    return 0;  
}</span>  

没有更多推荐了,返回首页