ACM 博弈知识 初步整理

边看  其他博主 的博客   博弈知识汇总  边整理一下 博弈论的相关知识。

首先 说一下博弈的一些主要分类:

(一)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规
定每次至少取一个,最多取m个。最后取光者得胜。

  分析:显然 当 n=m+1时,先手一次不能取完,后手都能直接取完。所以先手稳输。所以当n=(m+1)*r+s时,先手的只要拿掉s个或者(m+1)*k+s即可让后手面对 n=(m+1)*(r-k)的局面,这种局面是必输局。当其取k个时你就取m+1-k个,不难理解,最后一个取完的人肯定是你,所以当n=(m+1)*r+s时,(s=n%(m+1) ),先手必赢。

  当n=20,m=5时的SG表:

代码:

#include <stdio.h>
#include <algorithm>
#include <string.h>
const int maxn = 2000 + 10;
int f[maxn],SG[maxn],S[maxn];
void getSG(int n){
    memset(SG,0,sizeof(SG));
    for(int i=1; i<=n; i++){
        memset(S,0,sizeof(S));
        for(int j=1; f[j]<=i&& j<maxn; j++){
            S[SG[i-f[j]]] = 1;
        }
        for(int j=0; j<=n; j++)
            if(S[j]==0){
                SG[i]=j;
                break;
            }
    }
}
int main(){
    int n=20;
    int m=5;
    for(int i=1;i<=maxn;i++){
        if(i<=m)
            f[i]=i;
        else
            f[i]=m;
    }
    getSG(n);
    for(int i=0;i<=n;i++)
    {
        printf("%d**%d\n",i,SG[i]);
    }
    return 0;
}
View Code

 

  所以,当 n%(m+1) 为0时先手必输,否则先手必赢。

3、变形:条件不变,改为最后取光的人输。(即可看做谁先面对只剩1颗石子的局势必输,所以问题可转化为谁先取完(n-1)颗石子的人胜利,又变成了简单的巴什博奕)

结论:当(n-1)%(m+1)==0时后手胜利。

4、题目练习:HDOJ:2188 2149 1846

 

(二)威佐夫博奕(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同
时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

  ·分析:用二元组表示两堆石的数量(a,b)。并称之为局势。显然,如果甲面对(0,0),甲已经输了,那么(0,0)就成为奇异局势。手动可以推出前面几个奇异局势是(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int maxn = 1000 + 10;
int SG[maxn][maxn];
void get()
{
    int n=20;
    for (int i=0;i<=n;i++)
        for (int j=0;j<=n;j++)
        {
            int win=0,lose=0;
            for (int k=1;k<=min(i,j);k++)//同时从两堆中取相同多的物品
                if (SG[i-k][j-k]) win++; else lose++;
            for (int k=1;k<=i;k++)//从第一堆中取k个
                if (SG[i-k][j]) win++; else lose++;
            for (int k=1;k<=j;k++)//从第二堆中取k个
                if (SG[i][j-k]) win++; else lose++;
            if (lose==0)             //所有后继都是必胜态,当前是必败态
                printf("%d %d\n",i,j);
            else SG[i][j]=1;        
        }
}
int main(){
    get();
    return 0;
}
View Code

 

可以看出。a是前面未出现的最小自然数,b=a+k;(k是第k个奇异局势);

奇异局势的三个性质:

 1。任何自然数都包含在一个且仅有一个奇异局势中。
    由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak
-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
    2。任意操作都可将奇异局势变为非奇异局势。(必输局的后继全为必胜)
    事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其
他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由
于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
    3。采用适当的方法,可以将非奇异局势变为奇异局势。(必赢局的后继存在至少一个必败局)

    假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了
奇异局势(0,0);如果a = ak ,b > bk,那么,取走b  – bk个物体,即变为奇异局
势;如果 a = ak ,  b < bk ,则同时从两堆中拿走 ak – ab + ak个物体,变为奇异局
势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余
的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k)
,从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – a
j 即可。

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

  

那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?
我们有如下公式:a表示第一堆的个数,b表示第二堆的个数,c表示二者之差,当然a必须小于b;

再找规律的话我们会发现,a= c* 1.618 

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

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

这里的1.618最好用公式算出来。不然精确度可能会出错。

相关证明请看这篇博客:https://blog.csdn.net/wu_tongtong/article/details/79295069

 练习:poj 1067

(三)尼姆博奕(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的
物品,规定每次至少取一个,多者不限,最后取光者得胜。

  分析:同理用(a,b,c)表示局势,同理用SG函数打表出来

  

 

  代码如下:

  

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int maxn = 100 + 10;
int SG[maxn][maxn][maxn];
void get()
{
    int n=10;
    for (int i=0;i<=n;i++)
        for (int j=0;j<=n;j++)
        {
            for(int g=0;g<=n;g++)
            {
                int win=0,lose=0;
                //for (int k=1;k<=min(i,j);k++)//同时从两堆中取相同多的物品
                //    if (SG[i-k][j-k]) win++; else lose++;
                for (int k=1;k<=g;k++)//从第三堆中取k个
                    if (SG[i][j][g-k]) win++; else lose++;
                for (int k=1;k<=j;k++)//从第二堆中取k个
                    if (SG[i][j-k][g]) win++; else lose++;
                for (int k=1;k<=i;k++)//从第一堆中取k个
                    if (SG[i-k][j][g]) win++; else lose++;

                if (lose==0)             //所有后继都是必胜态,当前是必败态
                    printf("%d %d %d\n",i,j,g);
                else SG[i][j][g]=1;
            }

        }
}
int main(){
    get();
    return 0;
}
View Code

  对奇异局势进行分析:

  首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是
(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情
形。

 

那这种奇异局势有什么特点呢?
也不知谁这么牛逼,竟然能把这种局势和二进制联系在一起
这里说一种运算符号,异或'^',a^b=a'b+ab'(a'为非a)

 

我们用符号XOR表示这种运算,这种运算和一般加法不同的一点是1 XOR 1 = 0。先看(1,2,3)的按位模2加的结果:
1 = 二进制01
2 = 二进制10
3 = 二进制11  XOR
———————
0 = 二进制00 (注意不进位)
 
对于奇异局势(0,n,n)也一样,结果也是0
任何奇异局势(a,b,c)都有a XOR b XOR c = 0
 

如果我们面对的是一个非必败态(a,b,c),要如何变为必败态呢?

假设 a < b < c,我们只要将 c 变为a XOR b,即可。因为有如下的运算结果:
a XOR b XOR (a XOR b)=(a XOR a) XOR (b XOR b) = 0 XOR 0 = 0
要将c 变为a XOR b,只要对 c进行 c-(a XOR b)这样的运算即可。
 
为了进一步理解Nim取物品游戏,我们看看特殊情况。
如果游戏开始时只有一堆物品,先手则通过取走所有的物品而获胜。现在设有2堆物品,且物品数量分别为N1和N2。游戏者取得胜利并不在于N1和N2的值具体是多少,而是取决于它们是否相等。也就说两堆的策略我们有了,现在我们如何从两堆的取子策略扩展到任意堆数中呢?
 
首先回忆一下,每个正整数都有对应的一个二进制数,例如:57(10) = 111001(2) ,即:57(10)=2^5+2^4+2^3+2^0。于是,我们可以认为每一堆物品数由2的幂数的子堆组成。这样,含有57枚物品大堆就能看成是分别由数量为25、24、23、20的各个子堆组成。
 
现在考虑各大堆大小分别为N1,N2,……Nk的一般的Nim博弈。将每一个数Ni表示为其二进制数(数的位数相等,不等时在前面补0):
N1 = as…a1a0
N2 = bs…b1b0
……
Nk = ms…m1m0
如果每一种大小的子堆的个数都是偶数,我们就称Nim博弈是平衡的,而对应位相加是偶数的称为平衡位,否则称为非平衡位。因此,Nim博弈是平衡的,当且仅当:
as +bs + … + ms 是偶数,即as XOR bs XOR … XOR ms  = 0
……
a1 +b1 + … + m1 是偶数,即a1 XOR b1 XOR … XOR m1 = 0
a0 +b0 + … + m0是偶数,即a0 XOR b0 XOR … XOR m0 = 0
  
于是,我们就能得出尼姆博弈中先手获胜策略:
Bouton定理先手能够在非平衡尼姆博弈中取胜,而后手能够在平衡的尼姆博弈中取胜。即状态(x1, x2, x3, …, xn)为P状态当且仅当x1 xor x2 xor x3 xor … xor xn =0。这样的操作也称为Nim和(Nim Sum)。
        我们以一个两堆物品的尼姆博弈作为试验。设游戏开始时游戏处于非平衡状态。这样,先手就能通过一种取子方式使得他取子后留给后手的是一个平衡状态下的游戏,接着无论后手如何取子,再留给先手的一定是一个非平衡状态游戏,如此反复进行,当后手在最后一次平衡状态下取子后,先手便能一次性取走所有的物品而获胜。而如果游戏开始时游戏牌平衡状态,那根据上述方式取子,最终后手能获。
 
下面应用此获胜策略来考虑4堆的Nim博弈。其中各堆的大小分别为7,9,12,15枚硬币。用二进制表示各数分别为:0111,1001,1100和1111。
于是可得到如下一表:

 由Nim博弈的平衡条件可知,此游戏是一个非平衡状态的Nim博弈,因此,先手在按获胜策略一定能够取得最终的胜利。具体做法有多种,先手可以从大小为12的堆中取走11枚硬币,使得游戏达到平衡(如下表)

之后,无论后手如何取子,先手在取子后仍使得游戏达到平衡。

同样的道理,先手也可以选择大小为9的堆并取走5枚硬币而剩下4枚,或者,先手从大小为15的堆中取走13枚而留下2枚。
归根结底, Nim博弈的关键在于游戏开始时游戏处于何种状态(平衡或非平衡)和先手是否能够按照取子游戏的获胜策略来进行游戏
当堆数大于2时,我们看出Bouton定理依旧适用,下面用数学归纳法证明
  
证明:如果每堆都为0,显然是P状态(必败)。下面验证P状态和N状态的后两个递推关系:
一、每个N状态都可以一步到达P状态。
证明是构造性的。检查Nim和X的二进制表示中最左边一个1,则随便挑一个该位为1的物品堆Y,根据Nim和进行调整(0变1,1变0)即可。例如Nim和为100101011,而其中有一堆为101110001。为了让Nim和变为0,只需要让操作的物品堆Y和Nim的和异或,然后先手在第一次在Y中取走Y-(X xor Y) 个数目即可。显然操作后物品数变小,因此和合法的。设操作前其他堆的Nim和为Z,则有Y xor Z = X。操作后的Nim和为X xor Y xor Z = X xor X = 0,是一个P状态。
二、每个P状态(必胜态)都不可以一步到达P状态
由于只能改变一堆的物品,不管修改它的哪一位,Nim的对应位一定不为0,不可能是P状态。

 

nim博弈的另一种理解(个人觉得这个写得较好转自https://blog.csdn.net/lgdblue/article/details/15809893):

1、问题模型:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

2、解决思路:用(a,b,c)表示某种局势,显证(0,0,0)是第一种奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。

  搞定这个问题需要把必败态的规律找出:(a,b,c)是必败态等价于a^b^c=0(^表示异或运算)。

  证明:(1)任何p(a,b,c)=0的局面出发的任意局面(a,b,c’);一定有p(a,b,c’)不等于0。否则可以得到c=c’。

      (2)任何p(a,b,c)不等于0的局面都可以走向 p(a,b,c)=0的局面

       (3)对于 (4,9,13) 这个容易验证是奇异局势 

             

       其中有两个8,两个4,两个1,非零项成对出现,这就是尼姆和为  零的本质。别人要是拿掉13里的8或者1,那你就拿掉对应的9  中的那个8或者1;别人要是拿        掉13里的4,你就拿掉4里的4;  别人如果拿掉13里的3,就把10作分解,然后想办法满 足非零项成对即可。

3、推广一:如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a^b,即可,因为有如下的运算结果: a^b^(a^b)=(a^a)^(b^b)=0^0=0。要将c 变为a^b,只从 c中减去 c-(a^b)

4、推广二:当石子堆数为n堆时,则推广为当对每堆的数目进行亦或之后值为零是必败态。

5、练习:hdoj 1847

四、Fibonacci博弈

1、问题模型:

 有一堆个数为n的石子,游戏双方轮流取石子,满足: 

(1)先手不能在第一次把所有的石子取完; 

(2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。 约定取走最后一个石子的人为赢家。

2、解决思路:

  当n为Fibonacci数时,先手必败。即存在先手的必败态当且仅当石头个数为Fibonacci数。 

   

先看看FIB数列的必败证明:

 

 

 

1、当i=2时,先手只能取1颗,显然必败,结论成立。

 

2、假设当i<=k时,结论成立。

 

     则当i=k+1时,f[i] = f[k]+f[k-1]。

 

     则我们可以把这一堆石子看成两堆,简称k堆和k-1堆。

 

    (一定可以看成两堆,因为假如先手第一次取的石子数大于或等于f[k-1],则后手可以直接取完f[k],因为f[k] < 2*f[k-1])

 

     对于k-1堆,由假设可知,不论先手怎样取,后手总能取到最后一颗。下面我们分析一下后手最后取的石子数x的情况。

 

     如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。

 

     我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,由数学归纳法不难得出,后者大。

 

     所以我们得到,x<1/2*f[k]。

 

     即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。

 

     即i=k+1时,结论依然成立。

 

对于不是FIB数,首先进行分解。

 

 

 

分解的时候,要取尽量大的Fibonacci数。

 

比如分解85:85在55和89之间,于是可以写成85=55+30,然后继续分解30,30在21和34之间,所以可以写成30=21+9,

 

依此类推,最后分解成85=55+21+8+1。

 

则我们可以把n写成  n = f[a1]+f[a2]+……+f[ap]。(a1>a2>……>ap)

 

我们令先手先取完f[ap],即最小的这一堆。由于各个f之间不连续,则a(p-1) > ap  + 1,则有f[a(p-1)] > 2*f[ap]。即后手只能取f[a(p-1)]这一堆,且不能一次取完。

 

此时后手相当于面临这个子游戏(只有f[a(p-1)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。

 

同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。

 

3、练习题目:NYOJ 取石子游戏

 

 

五、公平组合博弈(Impartial Combinatori Games)

 

1、定义:

 

(1)两人参与。

 

(2)游戏局面的状态集合是有限。

 

(3)对于同一个局面,两个游戏者的可操作集合完全相同

 

(4)游戏者轮流进行游戏。

 

(5)当无法进行操作时游戏结束,此时不能进行操作的一方算输。

 

(6)无论游戏如何进行,总可以在有限步数之内结束。

 

2、模型:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。事实上,这个游戏可以认为是所有公平组合游戏(Impartial Combinatori Games)的抽象模型。其实,任何一个ICG都可以通过把每个局势看成一个顶点,对每个局势和它的子局势连一条有向边来抽象成这个“有向图游戏”。

 

3、解决思路:

 

现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P-状态和N-状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P-状态的和总是P-状态,P-状态和N-状态的和总是N-状态。但是两个N-状态的和既可能是P-状态也可能是N-状态。因此,只知道单个游戏的P-状态和N-状态是不够的。

 

为了正确地玩好游戏和我们需要推广P-状态和N-状态,它就是Sprague-Grudy函数(或者简称为g函数)

 

4、Sprague-Grudy定理:

 

令N = {0, 1, 2, 3, ...} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数.

 

形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数。定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

 

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | y是x的后继 }。

 

5、性质:

 

(1)所有的终结点所对应的顶点,其SG值为0,因为它的后继集合是空集——所有终结点是必败点(P点)。

 

(2)对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0——无论如何操作,从必败点(P点)都只能进入必胜点(N点)//对手走完又只能把N留给我们。

 

(3)对于一个g(x)!=0的顶点,必定存在一个后继点y满足g(y)=0——从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点)//就是那种我们要走的方法。

 

6、应用:

 

(1)可选步数为1-m的连续整数,直接取模即可,SG(x) = x % (m+1); 

 

(2)可选步数为任意步,SG(x) = x; 

 

(3)可选步数为一系列不连续的数,用mex(计算每个节点的值) 

 

7、练习:hdoj 1847 1536 3980

 

转载于:https://www.cnblogs.com/qq1028152659/p/9316151.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值