博弈——威佐夫博弈(hdu1527,2177)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1527

题目描述:

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。


首先介绍一下威佐夫博弈(Wythoff Game)的概念:

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

性质:两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

判断是否为奇异局势:任给一个局势(a,b), (下面用到的a是两者中较小的数)ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,…,n 方括号表示取整函数)

奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618…,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],

若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,

若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。


我们再用具体的数值说明一下:

当面对(0,0)时很显然我们是处于必败的状态,而下一个该状态即(1,2)

因为无论我们做(0,2)(1,1)(1,0)那种操作对方都能使我们变成(0,0)

以此类推,前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。

而他们的差值就是0,1,2,3,4,5,6,7……

所以第一个值 = 差值 * 1.618满足黄金分割数的比例。


这是为什么呢?

首先,若 (x, y) 是必败态,那么(x+i,y),((x,y+i)肯定是必胜态,因为我可以通过拿去i个石子使其变成(x,y)

这也就说明了,以后的必败态绝对不会再出现x、y。因为如果出现,我可以通过改变另一个值使它变为(x,y),那么就不是必败态了。

首先,假设我们得到状态 (a[i] - p, a[i] - p + y),并且比 a[i] 小的数都在之前出现过,若 a[i] - p 存在必败态 ,那么当前状态 (a[i] - p, a[i] - p + y)肯定是必胜态了

若 a[i] - p 出现在较大的数,由于小数是单增的,因而其对应的小数必小于 a[i] + y,故而也可推出其状态为必胜态。

于是,(a[i], a[i] +y) 状态的胜负性只与状态 (a[i], a[i] + d) (d

前出现过,所以得到的一定是一个必胜态,因而 (a[i], a[i] + y) 为必败态,即矩阵i 个必败态大数等于小数加上 i。

所以差值就是0,1,2,3,4,5,6,7……了


#include
#include
int main()
{
    int a,b;
    while(~scanf("%d%d",&a,&b))
    {
        double j,k,r;
        int t;
        if(a>b)
        {
            t=a;
            a=b;
            b=t;
        }
        r=(sqrt(5.0)-1)/2;
        j=(int)(r*a);
        if(a!=(int)(j/r))
        j+=1;
        if(b!=(int)(j/r)+j)
        printf("1\n");
        else
        printf("0\n");
    }
    return 0;
}

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2177

原理相同


#include
#include
int main()
{
	int a,b;
	while(~scanf("%d%d",&a,&b)&&(a||b))
	{
		double j,k,r,x;
		int i,n,m,m_n;
		int t;
		if(a>b)
		{
			t=a;
			a=b;
			b=t;
		}
		
		r=(sqrt(5.0)-1)/2;
		x=r+1;
		j=(int)(r*a);
		if(a!=(int)(j/r))
		j+=1;
		if(b==(int)(j/r)+j)
		printf("0\n");
		else
		{
		printf("1\n");
		for(i=1;i<=a;i++)
            {
                n=a-i;
                m=b-i;
                m_n=m-n;
                if((int)(m_n*x)==n)
                    printf("%d %d\n",n,m);
            }

        for(i=b-1;i>=0;i--)
            {
                n=a;
                m=i;
                if(n>m)
                {
                    t=n;
                    n=m;
                    m=t;
                }
                m_n=m-n;
                if((int)(m_n*x)==n)
                    printf("%d %d\n",n,m);
            }
        }

	    
	}
	return 0;
}







你可能感兴趣的:(ACM,nine_sun算法专栏)