zoukankan      html  css  js  c++  java
  • 博弈

    博弈可以看为找规律,不过找规律也是有先当多的套路,很久之前就接触过博弈了,不过一直没有整理,今天稍微整理一下三个基本的博弈。

    一、Bash Game:同余理论

    一堆n个物品,两人轮流取,每次取1至m个,最后取完者胜

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

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

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

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

     

    1.

    int Bash_Game(int n,int m)//是否先手有必赢策略
    
    {   
    
          if (n%(m+1)!=0)
    
                  return 1;
    
         return 0;
    
    }

    2.HDU2149

    /*只有当m小于n时才有可能多出价,否则就不可能给对手留下(n + 1)的局面了。*/
    /*输入m卖价和n加价,输出第一次可叫的价格*/
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int main()
    {
        int m, n;
        while(~scanf("%d %d", &m, &n))
        {
            if(m % (n + 1) == 0)
                puts("none");
            else
            {
                int i;
                if(m <= n)
                    for(i = m; i <= n; i++)
                    {
                        printf("%d", i);
                        if(i != n) putchar(' ');
                        else putchar('
    ');
                    }
                else
                    printf("%d
    ", m % (n + 1));
            }
        }
        return 0;
    }

    二、Nim Game:异或理论

    m堆n个物品,两人轮流取,每次取某堆中不少于1个,最后取完者胜

    所有物品数目二进制异或为0,则先手必输
    所有物品数目二进制异或不为0,则后手必输


    从另一个角度思考这个问题,如果物品数量随机,那么每个数目的每一位上1或0概率相同,
    如果有奇数个堆,那么1的个数为偶数或者奇数的概率相同,
    如果有偶数个堆,那么1的个数为偶数的概率略大1/(m+1),
    也就是说异或结果的每一位为0或1的概率几乎差不多,而先手必输要求异或结果每一位都为0,其实输的概率很小

    1.

    int Nimm_Game(int n)//假设n个数存在数组f[]中,有必胜策略返回1
    {
        int flag=0;
        for(int i=1; i<=n; i++)
            flag^=f[i];
        if(flag) 
            return 1;
        return 0;
    }

     

     

    2.

    /*游戏规则如上,如果先手的人能赢,输出他第一步可行的方案数,否则请输出0。*/
    /*输入堆数M,输入M个整数N分别表示M堆扑克的数量。M为0结束。*/
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    int a[200000+10];
    int main()
    {
        int N;
        while(scanf("%d",&N),N)
        {
            int ans=0,sum=0;
            memset(a,0,sizeof(a));
            for(int i=0; i<N; i++)
            {
                scanf("%d",&a[i]);
                ans^=a[i];
                sum+=a[i];
            }
            if(ans==0)
                printf("No
    ");
            else
            {
                printf("Yes
    ");
                //printf("%d
    ",sum-ans);
                for(int i=0; i<N; i++)
                {
                    int x=(a[i]^ans);//ans相当于剩余的个数
                    //x相当于去掉 a[i],即不取a[i]
                    //ans=0为先手必败态,先手取掉剩余的个数ans
                    //只要先手去掉,就可以使后手成为必败态的先手,即异或为0且先手先取
                    if(x<a[i])
                        //如果去掉 a[i]后剩的个数小于堆中的个数,便可取
                        //比如 2堆 3 1 为 必胜态,先手取掉 x即 3-1便可转化为先手必败的1 1
                        printf("%d %d
    ",a[i],x);
                }
            }
        }
        return 0;
    }

     

     

     

     

    三、Wythoff Game:黄金分割

    两堆(ak,bk)(ak<=bk)个物品,两人轮流取,每次从一堆中取k个或者从2堆中同时取k个,最后面对(0,0)局面的输(设ak<=bk是为了忽略顺序的影响)

    ak是前面必输局未出现的数中最小者,bk=ak+k( k=0,1,2,3,...n)

    下面介绍必输局(奇异局)的最重要性质:

    1,2,...,n中每一个自然数,出现且只出现在一个奇异局中。

    推导:1.由于ak总是选择未出现的数,所以每个数总能出现在奇异局且ak不会选择到重复的数

               2.bk=ak+k,所以bk总是比前面所有奇异局出现的数都大,所以bk不会选择到重复的数

               必赢一方的策略是:始终让对手面对必输局(奇异局)

    给定任意局势(a,b),判定(a,b)是否为必输局的方法是:

         k=0,1...n 记黄金比例是φ=1.618033
         ak=[k*φ],bk=ak+k=[k*φ*φ]
         如    k=0,ak=0,bk=0
                k=1,ak=1,bk=2
                k=2,ak=3,bk=5 

                k=3,ak=4,bk=7

    更好的一种判断策略是 k = bk-ak ,如ak=k*φ时,当前局势为奇异局

    从胜负概率角度,如果堆中数量随机,先手一方优势很大

    1.POJ1067

    /*游戏规则如上,你先取,如果最后你是胜者,则为1,反之,则为0。*/
    /*输入两个非负整数a和b,表示两堆石子的数目*/
    #include"stdio.h"
    #include"stdlib.h"
    #include"math.h"
    int main()
    {
        int a,b,t,ak;
        double k;
        k=(sqrt(5.0)+1.0)/2.0;
        //printf("%lf",k);
        while(scanf("%d%d",&a,&b)!=EOF)
        {
            if(a>b)
            {
                t=a;
                a=b;
                b=t;
            }
            t=b-a;
            ak=(int)(t*k);
            //printf("%d ",ak);
            if(a==ak)
                printf("0
    ");
            else
                printf("1
    ");
        }
    }
  • 相关阅读:
    个人作业2——英语学习APP案例分析
    结对编程1
    个人作业1——四则运算题目生成程序(基于控制台)
    个人附加作业
    个人作业3——个人总结(Alpha阶段)
    结对编程2--单元测试
    个人作业2——英语学习APP的案例分析
    结对编程1 李雯钰66、钱惠71
    个人作业1——四则运算题目生成程序(基于控制台)
    软件工程的实践项目课程的自我目标
  • 原文地址:https://www.cnblogs.com/aiguona/p/8351046.html
Copyright © 2011-2022 走看看