码迷,mamicode.com
首页 > 其他好文 > 详细

Games:取石子游戏(POJ 1067)

时间:2015-08-19 19:41:31      阅读:342      评论:0      收藏:0      [点我收藏+]

标签:

取石子游戏
 
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 37662   Accepted: 12594

Description

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

Input

输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。

Output

输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
 
2015-08-19
这道题比较有趣,代码量不多,但是里面的数学知识蕴含比较丰富。
这题就是著名的威佐夫博弈
 
如题所言,我们要做的就是要确定谁是最后的赢家,那么这题呢,取石子的方法有很多(千万不要被采取最好的策略给骗了,不是一定要取完的意思),我们总不可能一个一个地枚举把,那样太累了,也不可取,那怎么办呢?其实,在双人博弈中,只要是把棋子下完就算获胜的那种,且满足一定规则,那么一定存在有一方一定胜的情况,这个其实是图论的知识,我们等一下再讲,不急。
 
我们现在先观察一下,既然有一定是赢的情况,那么根据题意,我们最容易想到的就是(0,k),(k,0),(k,k)这三种情况,如果存在这三种情况,我们一定赢,那如果我们一定输的情况长什么样子的呢?我们想想,(1,2),(2,1),(3,5),(5,3)....这类的情况我们一定是输的,不信你可以试试看,我们就拿(1,2)来看,无论你把哪一边的石头取完,或者取一个之类的,你都是输。
 
这样看,貌似这题真的难透了,因为不仅存在一定赢的情况,而且也存在一定输的情况,还存在不一定赢也不一定输的情况,那完蛋了,这题是不是没法做了呢?别急,我们这样想:
 
如果你在必胜态上,不用想,你赢了。这种情况的前提是对手在必败态上
如果你要保证你自己赢,首先你自己就不能在必败态,而且最好你下完以后对手一定在必败态上,这种思路可以,
如果你不在必胜态上,你可以把自己转移到必胜态上,或者把对手逼到必败态上
 
这样一想那么这题就好做了,我们只用找必败态就好了,关键是怎么找呢?
带着这个疑问,我们来讲一下非常重要的模型:博弈的图论模型---必败态与核1
   TH1胜态一定可以通过某种策略走向必败态;而必败态采取任何策略都将走向胜态。
 
   图论语言:
   TH2:因为必败态只能走向胜态,所以任何两个必败态结点之间不可能存在边;
   TH3:因为胜态总能走到必败态,所以对任何一个非必败态的结点,
      一定存在一个从它指向必败态结点的边。

   定义1:有向图中,集合X中任意两点之间无边,称集合X为内固集。
   定义2:有向图中,任意不在集合X中的点存在一条指向集合X的边,称集合X为外固集。
   定义3:有向图中,集合X 既是外固集,又是内固集,称集合X为核。
 
   TH4:双人博弈中,约定走最后一步为胜,如果有核存在,则其中一方有不败策略。
   TH5:有限个结点的无回路有向图有唯一的核。
定理1定理2和定理3我们不证(这个根据图来理解比较好一点),
对于定理4,因为博弈肯定对应的是有向图,我们可以假设A先走,A总是做到会把B逼入核中的情况,然后B可以退出核外(假设还是存在往外指的边),总是存在A最后会把B逼到走投无路的情况(当然你可能会问如果B退出来以后A走投无路怎么办,那不可能,因为如果是这样,A早就在核中了,违反了核的定义)。同理B也是一样分析
对于定理5:因为图无回路,所以一定是有终点的,终点的集合即是核。
 
 
 
 
好了知道了图论的这个知识以后,回到题目来,那么这道题,必败态就是核的集合了,那怎么找到核呢?我们可以递推的思想,找一下规律
我们知道(0,0)是一个特殊的必败点,那么根据非必败点能走到必败点的结论,我们根据题目的条件,我们可以用3条线去掉非必败点,横线,竖线,和斜线,那么我们就去掉了(0,k),(k,0),(k,k)(k>1)的点,
接下来我们找第一个没有划的点,他一定是一个必败点,因为这个点不可能和(0,0)有交集,符合核的定义,我们找到了(2,1)和(1,2),我们也按照上面的处理方法,划线。
接下来就是(3,5),(5,3)这两个必败点,同理,然后就是(4,7)(7,4)………………………………一直往后推
技术分享
 
现在我们把这些点全部集中起来看,好了发现规律没?规律就是a(k)=b(k)+k,这个东西咱们写代码应该要熟悉,这个式子的a(k)和b(k)的比率,就是黄金分割率0.618(当然k要很大才是趋近这个数)
是不是有点奇怪?你可能会说,不行啊这只是我们画图推出来的规律,怎么可以拿来做结论呢?好吧,下面我们来稍微证明下:
 
证明{(a(k),b(k))|a(k)=b(k)+k}均在核中:
反证法:  
假设存在一个{(a(k),b(k))|a(k)=b(k)+k}不在核中,那么这个点呢一定会与某一个核存在边,那么边怎么来?要么就是横着过来,竖着过来,或者斜着过来(即存在另一个节点(c(n),d(n)),使得其坐标等于c(n)=a(k)-m或者d(n)=b(k)-m或者c(n)=a(k)-m,d(n)=b(k)-m),这样想肯定不对,因为在考虑(c(n),d(n))之前,我们已经把这些边和边上的节点全部排除了,
所以,{(a(k),b(k))|a(k)=b(k)+k}均在核中。证毕
 
另外,这个结论可以引出第二个非常著名的定理:Beatty定理2
 
Beatty定理是个什么东西,大家可以在下面的参考资料那里看看,
我们这里只讲结论,就是威佐夫博弈的必败点坐标满足Beatty定理并且如果a(n)=[αn],b(n)=[βn],则α=(sqrt(5)-1)/2;
 
那么接下来就是给大家看一下威佐夫博弈的代码,真的是简单的要死:
#include <stdio.h>
#include <math.h>

int main(void)
{
    long double p = (sqrt((long double)5) + (long double)1) / (long double)2;
    int a, b, k;

    while (~scanf("%d%d",&a,&b))
    {
        k = b > a ? b - a : a - b;
        a = b > a ? a : b;
        if (a == (int)(p*k)) printf("0\n");
        else printf("1\n");
    }
}

 

参考资料:

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明

1.http://blog.sina.com.cn/s/blog_727d57100100ql6e.html(我的那个证明方法是反证,他那个是数学归纳法,更严谨一点)

2.http://www.cnblogs.com/jackge/archive/2013/04/22/3034968.html

3.http://yjq24.blogbus.com/logs/42653430.html

4.http://yjq24.blogbus.com/logs/42826226.html

5.http://www.java3z.com/cwbwebhome/article/article19/res046.html

Games:取石子游戏(POJ 1067)

标签:

原文地址:http://www.cnblogs.com/Philip-Tell-Truth/p/4742415.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!