zoukankan      html  css  js  c++  java
  • 初学 博弈论 又称对策论 Game Theory

    博弈论 真的很有趣  ,回想起 前两天多校一道题的题解 所有不公平的游戏都存在必胜的玩法

     与人斗其乐无穷  https://vjudge.net/contest/241983#overview 博弈论专题

    一)巴什博奕(Bash Game):有n个石头,Alice和Bob轮流取石头,每次取的石头不能超过m个,Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁

     假设 n=m+1个,Alice 先取,无论她取多少个(>=1),Bob 都能一次性把剩下的取完,so ? Alice必输 

     情况就可以推广变成 如果 n= (m+1)*k  也就是 n%(m+1)==0 时,Alice 必输 ,反之 Alice 必赢  

    代码的话 实现很简单  hdu2188  hdu2149  

     1  #include<cstdio>
     2 int main(){
     3     int t,n,m;
     4     scanf("%d",&t);while(t--){
     5         scanf("%d%d",&n,&m);
     6         if(n%(m+1)==0)printf("Bob
    ");
     7         else printf("Alice
    ");
     8     }
     9     return 0;
    10 }
    View Code

    二)威佐夫博奕(Wythoff Game):有两堆石头,Alice和Bob轮流从某一堆取任意多的石头,或者从两堆中取相同多的石头,每次至少取一个石头, Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁(hdu 1846  hdu1527  51NOd1185)

    就首先引入奇异局势,也就是当面临其一局势的状态必输。 比如(0,0)就是一个奇异局势,面临这种情况必输。  接下来的我就不懂了。。。。- = .=    十分钟前 ,我现在模拟了几组,懂了

    前几个奇异局势有(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20) ...

    性质就是每个自然数都会出现在一个且唯一的一个奇异局势里面 ,非奇异局势可以经过适当操作转化为奇异局势 

       两个数 不是奇异局势 就是 非奇异局势  ,当Alice面对奇异局势时,必输,反之,必赢

    不是很懂的话 随便想两个数 模拟试试 多试几组就好了

    三)尼姆博弈

    有三堆石头,Alice和Bob轮流从其中一堆中取任意多的石头,每次至少取一个石头, Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁

    奇异局势:第一个奇异局势是(0,0,0),面对这种情况的一定输。

    设有a,b,c三堆石头,当a^b^c == 0时就是奇异局势,必输。
    非奇异局势一定能变为奇异局势,奇异局势一定能变成非奇异局势。
    证明:
    当a^b^c != 0时,a+b+c != 0,即还有石头。
    令a^b = x1,a^c = x2,b^c = x3,一定会存在至少下面的一种情况:
    x1 < c,x2 < b,x3 < a。
    假设x1 < c ,令c‘ =c – (c – x1) = x1,
    得a^b^c’ == 0。
    即非奇异局势变为奇异局势。

    当a^b^c == 0 时,即a^b = c
    只需要变成其中的任意一个数就可以当a^b != c。
    即奇异局势变为非奇异局势。
    由于(0,0,0)是最终得到的奇异局势,并且没有石头可取,所以面对a^b^c== 0的状态必输。

      就异或为零和不为零情况就是了 

    http://acm.hdu.edu.cn/showproblem.php?pid=1907   多组输入 一个棋盘上黑白的位置 问谁嬴谁输 不懂为啥得 a-b-1 ???

    走子方式,于是我们通过sg函数的定义,爆出sg函数值就发现sg函数值就是两个棋子的距离的绝对值减1.其实这个还是可以理解的,因为我们看当两个棋子相邻的时候,已经是一个必败态了。 ???

    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <algorithm>
    int main()
    {
        int n,m,a,b;
        while(scanf("%d%d",&n,&m)!=EOF){
            int cnt =0,flag =0;
            for(int i=0;i< n;i++){
                scanf("%d%d",&a,&b);
                if(a < b)std::swap(a,b);
                if(a > 1)flag++;cnt ^= abs(a-b-1);//???
            }
            printf(cnt==0 ?"BAD LUCK!
    ":"I WIN!
    ");
            
        }
        return 0;
    }
    View Code

     SG函数

    SG函数可以看成各个Nim游戏的和,将问题分成各种Nim问题,从而简化了问题。

    例如:有三堆石头,每次能取的数量为斐波拉系数,即只能取1,2,3,5,8,13,21……这些石头的数量。

    定义mex运算:表示最小的不属于这个集合的数。例如:mex{0,1,2,3} = 4、mex{0,1,3,4,5} = 2、mex{} = 0

    对于任意的x,Sg(x) = mex{S},S为x的后继状态数值的集合,假设有三个状态Sg(a)、Sg(b)、Sg(c)。那么Sg(x) = mex{Sg(a),Sg(b),Sg(c)}。

    设有三堆石头为x、y、z这三堆。那么只要判断Sg(x)^Sg(y)^Sg(z) == 0就行,当成立时就说明面对的是奇异局势,必输,反正必赢。 

    /* 摘自jumping_frog 博客 

    首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如

    mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

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

    sg[x]。

    例如:取石子问题,有1堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者胜利,那么各个数的SG值为多少?

    sg[0]=0,f[]={1,3,4},

    x=1时,可以取走1-f{1}个石子,剩余{0}个,mex{sg[0]}={0},故sg[1]=1;

    x=2时,可以取走2-f{1}个石子,剩余{1}个,mex{sg[1]}={1},故sg[2]=0;

    x=3时,可以取走3-f{1,3}个石子,剩余{2,0}个,mex{sg[2],sg[0]}={0,0},故sg[3]=1;

    x=4时,可以取走4-f{1,3,4}个石子,剩余{3,1,0}个,mex{sg[3],sg[1],sg[0]}={1,1,0},故sg[4]=2;

    x=5时,可以取走5-f{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}={2,0,1},故sg[5]=3;

    以此类推.....

       x         0  1  2  3  4  5  6  7  8....

    sg[x]      0  1  0  1  2  3  2  0  1....

    计算从1-n范围内的SG值。

    f(存储可以走的步数,f[0]表示可以有多少种走法)

    f[]需要从小到大排序

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

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

    3.可选步数为一系列不连续的数,用getSG()计算

    */

       不懂i  SG函数  先放着 过段时间再来看  如果是费波纳茨序列的话 只需数组开16 第十六个费波纳茨数就1196 

     打表  和  dfs   

    // 获得SG数组函数模板,t代表f数组的个数,n代表要求的sg数组上限
    // f数组就是能取的个数(对于此题就是Fibonacci数列
    // 有时,对于t已知就不需要单独传参
    void get_sg(int t,int n)
    {
        int i,j;
        memset(sg,0,sizeof(sg));
        for(i=1;i<=n;i++)
        {
            memset(mex,0,sizeof(mex));
            // 对于属于g(x)后继的数置1
            for( j=1 ;j<=t && fib[j]<=i ;j++ )
                mex[sg[i-fib[j]]]=1;
            // 找到最小不属于该集合的数
            for( j=0 ; j<=n ; j++ )
                if(!mex[j])
                    break;
            sg[i] = j;
        }
    }
    View 打表 Code
    //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
    //n是集合s的大小 S[i]是定义的特殊取法规则的数组
    int s[N],sg[N],n;
    bool vis[N];
    int dfs_SG(int x){
        if(sg[x] != -1)
            return sg[x];
        memset(vis,0,sizeof(vis));
        for(int i = 0; i < n; ++i){
            if(x >= s[i]){
                dfs_SG(x-s[i]);
                vis[sg[x-s[i]]] = 1;
            }
        }
        for(int i = 0;; ++i){
            if(!vis[i]){
                e = i;
                return sg[x] = i;
            }
        }
    }
    View dfs Code
    #include <bits/stdc++.h> // hdu 1848 
    using namespace std;
    const int N = 1010;
    int sg[N], mex[N];
    int fa[55]; 
    void getSG(int n) {
        for(int i = 1; i <= n; i ++) {
            memset(mex, 0, sizeof(mex));
            for(int j = 1; fa[j] <= i; ++ j)
                mex[sg[i-fa[j]]] = 1;
            for(int j = 0; ; j ++) {
                if(!mex[j]) {
                    sg[i] = j;
                    break;
                }
            }
        }
    }
    int main() {
        int a, b, c;
        fa[1] = 1, fa[2] = 2;
        for(int i = 3; i < 55; i ++) fa[i] = fa[i-1] + fa[i-2];
        getSG(1000);
        while(cin >> a >> b >> c) {
            if(a==0 && b==0 && c==0)break;
            if(sg[a] ^ sg[b] ^ sg[c])printf("Fibo
    ");
            else printf("Nacci
    ");
        }
        return 0;
    }
    View Code
    #include <bits/stdc++.h>
    using namespace std;
    
    const int  M = 105;
    int sg[M][M], vis[M], n, m;
    
    void fun()
    {
      memset(sg, 0, sizeof(sg));
    
      for (int i = 1; i <= m; i++)
        for (int j = 1; j <= m; j++)
        {
          if (i == j) continue;
    
          memset(vis, 0, sizeof(vis));
    
          if (i < j) {
            for (int k = i + 1; k < j; k++)
              vis[ sg[k][j] ] = 1, vis[ sg[i][k] ] = 1;
          }
          else if (i > j) {
            for (int k = j + 1; k < i; k++ )
              vis[ sg[k][j] ] = 1, vis[ sg[i][k] ] = 1;
          }
    
          for (int k = 0;; k++)
            if (!vis[k])  { sg[i][j] = k; break; }
        }
    }
    
    int main()
    {
      int a, b;
      while (cin >> n >> m)
      {
        int ans = 0;
        fun();
        while (n--) {
          scanf("%d%d", &a, &b);
          ans ^= sg[a][b];
        }
        if (ans == 0) cout << "BAD LUCK!" << endl;
        else cout << "I WIN!" << endl;
      }
    }
    View 同上 一道题 Code

    hdu1730  hdu1848  hdu1536 hdu2873

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

  • 相关阅读:
    django 数据库内容管理(admin)
    django QuerySet的缓存机制(高效使用)
    Uniapp 离线打包
    idea启动Tomcat时控制台中文乱码问题
    windows 在服务中启动tomcat 配置jvm参数
    new File("E:") 文件路径变成项目所在的文件夹
    window10 开启ftp服务端
    MySQL没有默认my.ini文件
    mysql5.7 修改sql_mode配置
    一台服务器,多个Tomcat运行相同的系统,一个浏览器访问时cookie相互影响
  • 原文地址:https://www.cnblogs.com/163467wyj/p/9399105.html
Copyright © 2011-2022 走看看