zoukankan      html  css  js  c++  java
  • 博弈论的算法总结

      开头先啰嗦一句:想学好博弈,必然要花费很多的时间,深入学习,不要存在一知半解,应该是一看到题目,就想到博弈的类型。

    以及,想不断重复不断重复,做大量各大oj网站的题目,最后吃透它。

    博弈:

      博弈论又被称为对策论(Game Theory),既是现代数学的一个新分支,也是运筹学的一个重要学科。

    博弈,具体的例子就是下棋,双方都考虑最有利于自已的步骤,但是最终必有一方输,一方赢。

      博弈的策略:参与者在行动之前所准备好的一套完整的行动方案,就是想好下完这步棋,对方会如何下,

    以及接下来该如何下,最终得出结果。

    常见的博弈有以下:

    1.博弈:合作博弈和非合作博弈
       合作博弈:指参与者能够达成一种具有约束力的协议,在协议范围内选择有利于双方的策略
       非合作博弈:指参与者无法达成这样一种协议
    2.博弈:静态博弈和动态博弈
       静态博弈:指在博弈中,参与者同时选择,或虽非同时选择,但是在逻辑时间
                       上是同时的。(期末老师评分与同学给老师评分)
       动态博弈:指在博弈中,参与者的行动有先后顺序,且后行动者能够观察
                       到先行动者的行动。(下棋)
    3.博弈:完全信息博弈与不完全信息博弈
       完全信息博弈:指在博弈中,每个参与者对其他参与者的类型,策略空间及损益函数都有准确的信息。(卖家与买家)
       不完全信息博弈:总有一些信息不是所有参与者都知道的
    4.博弈:零和博弈与非零和博弈
       零和博弈:指博弈前的损益总和与博弈后的损益总和相等
       非零和博弈:指博弈后的损益大于(小于)博弈前的损益总和(正和或负和 )

    下面我主要讲一些关于算法比赛中用到的博弈类型:

    首先你要理解必胜状态和必败状态:

      对下先手来说,

      一个状态是必败状态当且仅当它的所有后继都是必败状态。

      一个状态是必胜状态当且仅当它至少有一个后继是必败状态。

      就是说,博弈者,一旦捉住了胜利的把柄,必然最后胜利。

    博弈中常常用到的:

      两个数,不用中间变量实现交换。
      a b;
      a = a^b;
      b = a^b;
      a = a^b;

    巴什博弈:

    百度百科:

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

      显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。对于巴什博弈,那么我们规定,如果最后取光者输,那么又会如何呢?(n-1)%(m+1)==0则后手胜利

    先手会重新决定策略,所以不是简单的相反行的
    例如n=15,m=3
    后手 先手 剩余
    0 2 13
    1 3 9
    2 2 5
    3 1 1
    1 0 0
    先手胜利 输的人最后必定只抓走一个,如果>1个,则必定会留一个给对手
     

    请去刷下面的题目,均是巴什博弈

         算博弈题目时,一定要算到一个周期结束,防止出错,很有可能像HDU2897那样。中途错的猝不及防    

     HDU1847 
     
    代码实现如下:
    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int n = cin.nextInt();
                PLG(n);
            }
        }
        static void PLG(int n)
        {
            if(n%3 == 0)
            {
                System.out.println("Cici");
            }
            else
            {
                System.out.println("Kiki");
            }
        }
    }
    代码实现如下:
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int n = cin.nextInt();
                int m = cin.nextInt();
                if(n == 0 && m == 0)
                {
                    return;
                }
                PLG(n,m);
            }
        }
        static void PLG(int n,int m)
        {
            if(n%2 == 0 || m % 2 == 0)
            {
                System.out.println("Wonderful!");
            }
            else
            {
                System.out.println("What a pity!");
            }
        }
    }
    代码实现如下:
    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int m = cin.nextInt();
                int n = cin.nextInt();
                PLG(m,n);
            }
        }
        static void PLG(int m,int n)
        {
            if(m % (n+1) == 0)
            {
                System.out.println("none");
            }
            else
            {
                if(m <= n)
                {
                    for(int i = m; i <= n; i++)
                    {
                        if(i!= m)
                        {
                            System.out.print(" ");
                        }
                        System.out.print(i);
                    }
                    System.out.println();
                }
                else
                {
                    int flag = 0;
                    for(int i = 1; i <= n; i++)
                    {
                        if((m-i)%(n+1) == 0)
                        {
                            if(flag == 0)
                            {
                                System.out.print(i);
                            }
                            else
                            {
                                System.out.print(" " + i);
                            }
                            flag++;
                        }
                    }
                    System.out.println();
                }
            }
        }
    }
    代码实现如下:
    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            int C = cin.nextInt();
            while(C != 0)
            {
                int n = cin.nextInt();
                int m = cin.nextInt();
                PLG(n,m);
                C--;
            }
        }
        static void PLG(int n,int m)
        {
            if(n <= m)
            {
                System.out.println("Grass");
            }
            else
            {
                if(n % (m+1) == 0)
                {
                    System.out.println("Rabbit");
                }
                else
                {
                    System.out.println("Grass");
                }
            }
        }
    }
     
     
    代码实现如下:
    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int n = cin.nextInt();
                int p = cin.nextInt();
                int q = cin.nextInt();
                PLG(n,p,q);
            }
        }
        static void PLG(int n,int p,int q)
        {
            if(n < p+q)
            {
                if(n <= p)
                {
                    System.out.println("LOST");
                }
                else
                {
                    System.out.println("WIN");
                }
            }
            else if(n%(p+q) == 0)
            {
                System.out.println("WIN");
            }
            else//有坑
            {
                if(n % (p+q) > p)
                    System.out.println("WIN");
                else
                    System.out.println("LOST");
            }
        }
    }

    威佐夫博弈:

      一定要去百度百科上面,先理解透意思。

      下面是一些威佐夫博弈的总结:

      威佐夫博弈(Wythoff's game):有两堆各若干个物品,两个人轮流从某一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
    这种情况下是颇为复杂的。我们用(a[k],b[k])(a[k] ≤ b[k] ,k=0,1,2,...,n)(表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。
    前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。注:k表示奇异局势的序号, 第一个奇异局势k=0。
    可以看出,a[0]=b[0]=0,a[k]是未在前面出现过的最小自然数,而 b[k]= a[k] + k。
    重要结论:(a,b)b>= a的,如果(int)(b-a)*(Math.sqrt(5)+1)/2 == a,那么先手必输。
    如果不等,后者必输。
     

    设当前局势为(a,b); a <= b
    ①a==b:同时从两堆取走a个石子,转化为(0,0)
    ②a==a[k]&&b>b[k]:从第二堆取走b−b[k]个石子,转化为(a,b[k])
    ③a==a[k]&&b<b[k]:同时从两堆取走a−a[b−a]个石子,转化为(a[b−a],b−a+a[b−a])
    ④a>a[k]&&b==b[k]:从第一堆取走a−a[k]个石子,转化为(a[k],b)
    ⑤a<a[k]&&b==b[k]:若a==a[j] (j<k),则从第二堆取走b−b[j]个石子,转化为(a,b[j]);

      否则必有a==b[j](j<k)a==b[j](j<k),则从第二堆取走b−a[j]b−a[j]个石子,转化为(a[j],a)

      例如5  8 ,5>a(8-5)=a3=4  8>b(8-5)=b3=7 从两堆中取走a-a(b-a)=5-4=1个,变成奇异局势(4,7)。

      例如4 6 ,4>a(6-4)=a2=3  6>b(6-4)=b2=5 从两堆中取走a-a(b-a)=4-3=1个,变成奇异局势(3,5)。

      可以理解成变成差为b-a的奇异局势。

    如果a=bk并且b-a!=k,则从b堆中取走b-ak个,变成奇异局势(ak,bk).

      例如,5 10 ,5=b2 10-5=5!=2 则从10中取走10-a2=10-3=7个,变成奇异局势(3,5)。

    为什么要b-a!=k呢?例如7 10 , 7=a3,10-7=3=k 也可以变成奇异局势(4,7)。但这已经在4)判断过了。

      奇异局势就是当你面临这种情况的时候,你必然是输的,反之,你必赢。
      (a,b),a,b两堆物品的重量,此处是b>a;
      解题的技巧:
      if a > b , 交换两个值。
      c = b-a;
      c = (int)(c*((根号5)+1)/2)
      if(c == b)  先手必输
      else 先手必赢
      题目:HDU1527 HDU2177特别要注意HDU2177这道题目。
     
    HDU1527的代码实现如下:
    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        static final double mid = (Math.sqrt(5)+1)/2;
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int a = cin.nextInt();
                int b = cin.nextInt();
                int MAX = Math.max(a, b);
                int MIN = Math.min(a, b);
                int temp = (int) ((MAX-MIN)*mid);
                if(temp == MIN)
                {
                    System.out.println("0");
                }
                else
                {
                    System.out.println("1");
                }
            }
        }
    }

    HDU2177的代码实现如下:巧妙暴力,分情况太麻烦了。

    package Combat.com;

    import java.util.Arrays;
    import java.util.Scanner;

    public class Main
    {
        static final double MID = (Math.sqrt(5)+1)/2;
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int a = cin.nextInt();
                int b = cin.nextInt();
                if(a == 0 && b == 0)
                {
                    return;
                }
                WTFGame(a,b);
            }
        }
        static void WTFGame(int a,int b)
        {
            int temp = (int) ((b-a)*MID);
            if(temp == a)
            {
                System.out.println("0");
            }
            else
            {
                System.out.println("1");
                for(int i = 1; i <= a; i++)//先取同样石子
                {
                    int n = a-i;
                    int m = b-i;
                    temp = (int) ((m-n)*MID);
                    if(temp == n)
                    {
                        System.out.println(n + " " + m);
                    }
                }
                for(int i = a-1; i >=0; i--)//从最小堆单取;
                {
                    temp = (int) ((b-i)*MID);
                    if(temp > i)//因为a越小,temp就越大,永远不可能等。
                    {
                        break;
                    }
                }
                for(int i = b-1; i >= 0; i--)//从最大堆单取
                {
                    int n = a;
                    int m = i;
                    if(n > m)
                    {
                        int t = a;
                        n = m;
                        m = t;
                        temp = (int)((m-n)*MID);
                        if(temp > n)//这里充当优先,当条件满足,无需进行下去了。
                        {
                            break;
                        }
                    }
                    temp = (int) ((m-n)*MID);
                    if(temp == n)
                    {
                        System.out.println(n + " " + m);
                    }
                }
            }
        }
    }

    尼姆博弈(Nimm Game):

    尼姆博弈指的是这样一个博弈游戏:  有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,  取到最后一件物品的人获胜。

    百度百科:

      有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
      这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是  (0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论自己如何拿,接下来对手都可以将其变为(0,n,n)  的情形。
    计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号⊕表示这种运算,先看(1,2,3)的按位模2加的结果:
    1 =二进制01
    2 =二进制10
    3 =二进制11 ⊕
    ———————
    0 =二进制00 (注意不进位)
    对于奇异局势(0,n,n)也一样,结果也是0。
    任何奇异局势(a,b,c)都有a⊕b⊕c =0。
    注意到异或运算的交换律和结合律,及a⊕a=0,:
    a⊕b⊕(a⊕b)=(a⊕a)⊕(b⊕b)=0⊕0=0。
    所以从一个非奇异局势向一个奇异局势转换的方式可以是:
    1)使 a = c⊕b
    2)使 b = a⊕c
    3)使 c = a⊕b

    结论就是:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。

    HDU2176

    代码实现如下;

    package Combat.com;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main
    {
        static final int MAX = 200005;
        static int array[] = new int[MAX];
        public static void main(String []args)
        {
            Scanner cin = new Scanner(System.in);
            while(cin.hasNext())
            {
                int m = cin.nextInt();
                if(m == 0)
                {
                    return;
                }
                int sum = 0;//异或的结果
                for(int i = 0; i < m; i++)
                {
                    array[i] = cin.nextInt();
                    sum = sum^array[i];
                }
                NIMGame(sum,m);
            }
        }
        static void NIMGame(int sum,int k)
        {
            if(sum == 0)//代表面临奇异情况,必输
            {
                System.out.println("No");
                return;
            }
            else
            {
                System.out.println("Yes");
                for(int i = 0; i < k; i++)//胜的第一次取法,
                {
                    int s = sum^array[i];//结果s相当于,sum没与array[i]异或。
                    if(s < array[i])
                    {
                        System.out.println(array[i] + " " + s);
                    }
                }
            }
        }
    }

    HDU1850  也是一道尼姆博弈题目,解法和上面相同。

    HDU1907也是同样的做法

     阶梯博弈:

    具体意思,参照下面网址:https://www.cnblogs.com/jiangjing/p/3849284.html

      

      

  • 相关阅读:
    Pixysoft.Weblications.Notebooks 开发实录
    Pixysoft.Framework.Noebe.Recovery
    搞一个动态加载dll竟然搞了半天,郁闷。动态加载 卸载 Assembly, Appdomain。
    趁着09年还没有结束,写下我对10年的心愿
    再次出现系统更新失误
    自动备份恢复框架开发小结
    Pixysoft.Framework.Pageflows 页面流开发实录
    代码混淆软件 DotFuscator 非常严重的问题,放在首页通知一下各位。
    20100111 一次非常严重的开发事故
    自己orm框架的一个旷世大BUG!自己都恶心死了。
  • 原文地址:https://www.cnblogs.com/674001396long/p/9901811.html
Copyright © 2011-2022 走看看