zoukankan      html  css  js  c++  java
  • 博弈论

    (1)巴什博弈

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

    这个游戏是有规律的,并且每个人都采取理智最优的话,游戏一开始就决定了胜负。

    与之类似的是报数,从1开始报,最少报数加一个,最多报数加3个,谁先报到20谁赢。

    第一个人无论是报1,2,3,第二个人每次取4的倍数都是可以的。如此往复一定是第二个人赢。

    即存在公式:N=(K+1)*X+M

    在本例中K=3,N=20,那么X=5,M=0,如此一来一定是第二个人赢,而如果M!=0,则第一个人报M,第二个人无论报多少,第一个人再重新报M+(K+1),如此第一个人是必胜的。

    例子:

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

    代码:

     1 #include<iostream>
     2 using namespace std;
     3 
     4 int main()
     5 {
     6     int N,K;
     7     while(scanf("%d%d",&N,&K))
     8     {
     9         if(N==0)break;
    10         else
    11         {
    12             if((N-1)%(K+1)==0)
    13                 printf("Jiang
    ");
    14             else
    15                 printf("Tang
    ");
    16         }
    17     }
    18     return 0;
    19 }

    (2)威佐夫博弈

    有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

    简单判断威佐夫博弈胜负:

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

    需要输出第一步取石子策略:

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

    关于这个博弈是存在必败格局的,而且必败格局有一定的规律。两堆石子等效,故而假设第一堆数目少与等于第二堆。

    第一个必败格局为:(0,0),即如果假设A当前面对此数目石子,说明B已经在上一步骤取完。

    第二个必败格局为:(1,2),如果A当前面对此数目石子,A取完为(1,1)或(0,2),B取完之后A面对的格局依旧是(0,0)

    第三个必败格局为:(3,5),依次列举为:(4,7)(6,10)。。。

    对于威佐夫博弈有如下规律:

    我们用a[i]表示失败态中的第一个,b[i]表示失败态中的第二个.(i从0开始).

    那么我们可以看到b[i] = a[i]+i;(i >= 0),a[i]是前面的失败态中没有出现过的最小的整数。

    1.每个数仅包含在一个失败态中

    可以根据递推式得到此性质

    2.每个失败态可以转到非失败态

    3.每个非失败态都可以转到一个失败态

        k=b-a;if (a-ak)=(b-bk); (a-ak)>0,(b-bk)>0

    则转化为(a-(a-ak),b-(b-bk));

         在ak中可以找到与a相等的,或bk中可以找到与a相等的,则转化为(ak,bk);

    这个性质决定了,如果当前不是失败态,我们如何让对手输掉这场博弈。

    4.可以用下面的方法来判断失败态

    a[i] = [i*(1+√5)/2](这里的中括号表示向下取整),b[i]=a[i]+i;

    那么这就是一个失败态。

    利用性质4解决第一个问题:

    #include<iostream>
    #include<cmath>
    using namespace std;
    
    void change(int &a,int &b)
    {
        int t=a;
        a=b;
        b=t;
    }
    int main()
    {
        int a,b;
        while(scanf("%d%d",&a,&b)!=EOF)
        {
            if(a>b)
                change(a,b);
            double x=(sqrt(5.0)+1)/2;
            if((int)((b-a)*x)==a)
                printf("0
    ");
            else
                printf("1
    ");
        }
        return 0;
    }

    利用性质3和性质4解决第二个问题:

     首先打必败局表,然后写一个二分查找用来定位,再然后就是利用性质3的两条,分别将所有可能的先手步骤打印出来。

    思路用的是讨论区最初的那个思路

    http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=12441&messageid=1&deep=0

    虽然可以AC,但是许多人也提到了这个题目的数据比较水,容易通过。

    #include<iostream>
    #include<cmath>
    using namespace std;
    #define MAX 1000001
    int a[MAX],b[MAX];
    int record[MAX+MAX]={0};
    int num;
    void calc()
    {
        int cnt=0;
        record[0]=1;
        int i;
        for(i=1;a[i-1]<=MAX;i++)
        {
            while(record[cnt]!=0)
                cnt++;
            a[i]=cnt;
            b[i]=cnt+i;
            record[a[i]]=1;record[b[i]]=1;
        }
        num=i;
    }
    int findA(int num,int l,int r)
    {
        int mid=(l+r)/2;
        if(a[mid]==num)
            return mid;
        else
        {
            if(l>=r)return 0;
            if(a[mid]>num)
                findA(num,l,mid-1);
            else
                findA(num,mid+1,r);
        }
    }
    int findB(int num,int l,int r)
    {
        int mid=(l+r)/2;
        if(b[mid]==num)
            return mid;
        else
        {
            if(l>=r)return 0;
            if(b[mid]>num)
                findB(num,l,mid-1);
            else
                findB(num,mid+1,r);
        }
    }
    int main()
    {
        
        calc();
    
        int one,two;
        while(scanf("%d%d",&one,&two))
        {
            if(one==0&&two==0)break;
            double x=(sqrt(5.0)+1)/2;
            if((int)((two-one)*x)==one)
                printf("0
    ");
            else
            {
                printf("1
    ");
                int k=two-one;
                if((one-a[k]==two-b[k])&&(one-a[k])>0&&(two-b[k])>0)
                    printf("%d %d
    ",one-(one-a[k]),two-(two-b[k]));
                int onepro=findA(one,0,num-1);
                if(onepro!=0)
                    printf("%d %d
    ",one,b[onepro]);
                else
                {
                    int oneB=findB(one,0,num-1);
                    printf("%d %d
    ",a[oneB],one);
                }
    
                
            }
        }
    
        return 0;
    }

    输入:5 8

    输出:

    1

    4 7

    3 5

  • 相关阅读:
    string::push_back()
    string::pop_back
    string::insert
    string::get_allocator
    opencv —— minEnclosingCircle、fitEllipse 寻找包裹轮廓的最小圆、点集拟合椭圆
    opencv —— boundingRect、minAreaRect 寻找包裹轮廓的最小正矩形、最小斜矩形
    opencv —— approxPolyDP 生成逼近曲线
    opencv —— convexHull 寻找并绘制凸包
    opencv —— findContours、drawContours 寻找并绘制轮廓
    opencv —— equalizeHist 直方图均衡化实现对比度增强
  • 原文地址:https://www.cnblogs.com/holyprince/p/4263245.html
Copyright © 2011-2022 走看看