博弈论 三大游戏

Bash 游戏 

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。

假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。

 

基础:1      ,      2, ...,        m是必赢局面,   m+1是必输局面
递推:m+2,m+3, ... ,2m+1是必赢局面,2m+2是必输局面 
             ...

            k(m+1)是必输局面,应该允许k=0,因为0显然也是必输局面    

在必输局和必赢局中,赢的一方的策略是: 拿掉部分物品,使对方面临k(m+1)的局面 

例如上例中10个物品,只能拿1到5个,先手方拿4个即可,对手无论拿多少个,你下次总能拿完

从另一个角度思考这个问题,如果物品数量随机,那么先手一方胜利的概率是m/(m+1),后手方胜利的概率是1/(m+1)

 

 1 #include <ctype.h>
 2 #include <cstdio>
 3 
 4 int n,x,y;
 5 
 6 inline void read(int&x) {
 7     register char c=getchar();
 8     for(x=0;!isdigit(c);c=getchar());
 9     for(;isdigit(c);x=x*10+c-48,c=getchar());
10 }
11 
12 int hh() {
13     read(n);
14     while(n--) {
15         read(x);read(y);
16         if(x%(y+1)==0) printf("B\n");
17         else printf("A\n"); 
18     }
19     return 0;
20 } 
21 
22 int sb=hh();
23 int main() {;}
Bash

 

Nim 游戏

 

有N堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N及每堆石子的数量,问最后谁能赢得比赛。
例如:3堆石子,每堆1颗。A拿1颗,B拿1颗,此时还剩1堆,所以A可以拿到最后1颗石子。
 
 
 
所有物品数目二进制异或    为0,则先手必输
所有物品数目二进制异或不为0,则后手必输
从另一个角度思考这个问题,如果物品数量随机,那么每个数目的每一位上1或0概率相同,
如果有奇数个堆,那么1的个数为偶数或者奇数的概率相同,
如果有偶数个堆,那么1的个数为偶数的概率略大1/(m+1),
也就是说异或结果的每一位为0或1的概率几乎差不多,而先手必输要求异或结果每一位都为0,其实输的概率很小
 
 1 #include <ctype.h>
 2 #include <cstdio>
 3 
 4 inline void read(int&x) {
 5     register char c=getchar();
 6     for(x=0;!isdigit(c);c=getchar());
 7     for(;isdigit(c);x=x*10+c-48,c=getchar());
 8 }
 9 
10 int n,x,ans;
11 
12 int hh() {
13     read(n);
14     read(ans);
15     for(int i=2;i<=n;++i) {
16         read(x);
17         ans^=x;
18     }
19     if(ans==0) puts("B");
20     else puts("A");
21     return 0;
22 } 
23 
24 int sb=hh();
25 int main() {;}
Nim

 

Wythoff 游戏

 

有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。
拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。
给出2堆石子的数量,问最后谁能赢得比赛。
 

第一个(0 , 0),先手输,当游戏某一方面对( 0 , 0)时,他没有办法取了,那么肯定是先手在上一局取完了,那么输。

第二个 ( 1  , 2  ),先手输,先手只有四种取法,

1)取 1 中的一个,那么后手取第二堆中两个。

2)取 2 中一个,那么后手在两堆中各取一个。

3)在 2 中取两个,那么后手在第一堆中取一个。

4)两堆中各取一个,那么后手在第二堆中取一个。

可以看出,不论先手怎么取,后说总是能赢。所以先手必输!

第三个 ( 3 , 5 ),先手必输。首先先手必定不能把任意一堆取完,如果取完了很明显后手取完另一堆先手必输,那么

假如看取一堆的情况,假设先手先在第一堆中取。 取 1 个,后手第二堆中取4个,变成(1 ,2)了,上面分析了是先手的必输局。

 取 2 个,后手第二堆中取3个,也变成( 1 , 2)局面了。

假设先手在第二堆中取,取 1 个,那么后手在两堆中各取 2 个,也变成 ( 1 , 2 )局面了。

   取 2 个 ,那么后手可以两堆中都去三个, 变成 ( 0 , 0)局面,上面分析其必输。

   取  3  个,后手两堆各取 1 个 ,变成( 1 , 2)局面了。

  取 4 个,后手在第一堆中取一个,变成( 1 , 2)局面了。

可见不论先手怎么取,其必输!

第四个(4  , 7),先手必输。

自己推理可以发现不论第一次先手如何取,那么后手总是会变成前面分析过的先手的必输局面。

那么到底有什么规律没有呢,我们继续往下写。

第四个 ( 6 ,10  )

第五个 ( 8 ,13)

第六个 ( 9 , 15)

第七个 ( 11 ,18)

会发现他们的差值是递增的,为 0 , 1 , 2, 3, 4 , 5 , 6, 7.....n

而用数学方法分析发现局面中第一个值为前面局面中没有出现过的第一个值,比如第三个局面,前面出现了 0  1 2,那么第三个局面的第一个值为 3 ,比如第五个局面,前

面出现了 0  1  2 3 4 5 ,那么第五个局面第一个值为6。

再找规律的话我们会发现,第一个值 = 差值 * 1.618 

而1.618 = (sqrt(5)+ 1) /  2 。

大家都知道0.618是黄金分割率。而威佐夫博弈正好是1.618,这就是博弈的奇妙之处!

 

 

下面来看看威佐夫博弈常见的三类问题:

 

1)给你一个局面,让你求是先手输赢。

有了上面的分析,那么这个问题应该不难解决。首先求出差值,差值 * 1.618 == 最小值 的话后手赢,否则先手赢。(注意这里的1.618最好是用上面式子计算出来的,否则精

度要求高的题目会错)

 

2)给你一个局面,让你求先手输赢,假设先手赢的话输出他第一次的取法。

       首先讨论在两边同时取的情况,很明显两边同时取的话,不论怎样取他的差值是不会变的,那么我们可以根据差值计算出其中的小的值,然后加上差值就是大的一个值,当

然能取的条件是求出的最小的值不能大于其中小的一堆的石子数目。

      加入在一堆中取的话,可以取任意一堆,那么其差值也是不定的,但是我们可以枚举差值,差值范围是0 --- 大的石子数目,然后根据上面的理论判断满足条件的话就是一种合理的取法。

 
 
 1 #include <ctype.h>
 2 #include <cstdio> 
 3 #include <cmath>
 4 
 5 int t,n,m;
 6 
 7 inline void read(int&x) {
 8     register char c=getchar();
 9     for(x=0;!isdigit(c);c=getchar());
10     for(;isdigit(c);x=x*10+c-48,c=getchar());
11 }
12 
13 inline void swap(int&a,int&b) {
14     int t=a;
15     a=b;
16     b=t;
17     return;
18 }
19 
20 int hh() {
21     read(t);
22     while(t--) {
23         read(n);read(m);
24         if(n>m) swap(n,m);
25         int temp=(m-n)*(sqrt(5)+1)/2;
26         if(temp==n) puts("B");
27         else puts("A"); 
28     }
29     return 0;
30 }
31 
32 int sb=hh();
33 int main() {;}
Wythoff

 

posted @ 2017-08-14 21:11  拿叉插猹哈  阅读(1531)  评论(0编辑  收藏  举报