威佐夫博弈

博弈规则:有两堆各若干个物品,两个人轮流从某一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

a堆和b堆的个数用(a,b)表示

奇异局势:面对的时候,就是输家,比如(0,0),(1,2),(3,5),(4,7),(6,10)...

举例(0,0),规则上说取光者胜,轮到你的时候是(0,0),说明你输了。

举例(1,2),如果拿走一个,变成(1,1)或者(0,2),对手可以同时从两堆拿1个或者b堆拿2个,你都会输,如果拿走两个,变成(0,1)或者(1,0),对手随便拿走一个,你都会输。面对(1,2),必输。

举例(3,5),如果拿走一个,变成(2,5)或者(3,4),对手可以拿走b堆2个变成(2,1),让你面对奇异局势,也可以同时从a、b堆拿走2个,变成(1,2)让你面对奇异局势。如果拿走2个,可以变成(1,5),(2,4),(3,2),根据规则对手可以变成奇异局势(1,2),(2,1),(1,2)。必输...

任何非奇异局势都可以转变成奇异局势,先手面对的如果是奇异局势必输,如果不是奇异局势必赢。那么如何判断奇异局势呢?a =[k(1+√5)/2],b= a + k (k=0,1,2,...n 方括号表示取整函数)

当k=0时,有(0,0)

当k=1时,有(1,2)

当k=2时,有(3,5)

当k=3时,有(4,7)

当k=4时,有(6,10)

......

观察一下可以发现,每个自然数只出现过一次,并且奇异局势的两个数的差就是k值,所以只要判断a是否符合公式就可以了。

 

hdu2177:取(2堆)石子游戏

代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
const double sq=(1+sqrt(5.0))/2.0;///浮点数再取整

int main()
{
    int a,b,c;
    while(cin>>a>>b&&(a+b))
    {
        c=b-a;
        if( a==(int)(c*sq) && b==a+c )
            cout<<0<<endl;
        else
        {
            cout<<1<<endl;
            for(int i=1;i<=a;i++)///从两边那走一样多的石头
                if( a-i==(int) (c*sq) )///(int)(c*sq)对乘积整体取整
                printf("%d %d\n",a-i,b-i);

            for(int i=1;i<=b-a;i++)///从b堆拿走石头到b堆和a堆一样多,a比b少
                if( a==(int)  ((b-i-a)*sq)    )
                printf("%d %d\n",a,b-i);

            b=a;                    ///把b的石头数置为a,然后从a堆中拿,a还是比b少

            for(int i=1;i<a;i++)
                if( a-i==(int) ((b-a+i)*sq) )
                printf("%d %d\n",a-i,b);
        }
    }
    return 0;
}

 

posted @ 2018-08-14 23:46  守林鸟  阅读(318)  评论(0编辑  收藏  举报