zoukankan      html  css  js  c++  java
  • 博弈论题目总结(一)——简单组合游戏

    人类的本质是什么呢?复读机?鸽子?

    博弈问题是很有意思的一类题目

    我讲的可能不是很明白,但题目都不难建议自己思考

    组合游戏的特点:

    1.两个人博弈,轮流做出最优决策

    2.玩家在每个时刻做出的决策都是能预测到的,是一个确定的集合

    3.每种状态可能有多种方式到达,但同一种状态不能在一次游戏中重复到达,且没有平局的情况

    4.只要能进行决策,就一定要决策,不能跳过这个回合

    SG组合游戏

    我们把每种状态抽象成一个点,在起点有一颗棋子,两个人选取最优策略轮流对这颗棋子进行移动,最后不能移动棋子的人失败

    显然这张图是一个有向无环图

    (以下用集合的名称代指集合内的点)

    有向图的核

    给定一张DAG图<V,E>,如果V的一个点集S满足:

    1.S是独立集

    2.集合V-S中的点可以通过一步到达集合S中的点

    那么S是图V的一个核

    结论:核内节点对应SG组合游戏的必败态

    Alice把棋子从S移动到V-S

    然后Bob又通过一步把棋子从V-S移动到了S

    Alice像是被支配了一样,被迫把棋子移动到了没有出度的必败节点,Bob胜利

    说多了也没什么用,还是看题吧

    默认Alice先手,Bob后手

    注意,本文讨论的先手后手是针对状态而言的,而不是整局游戏

    POJ 2368 Buttons (巴什博弈)

    题面:

    两个人玩游戏,有n个石子,两个人轮流取,每次取[1,L]个石子,取走最后一个石子的人胜利。假设两人秃顶聪明,问L为何值时第二个人必胜,输出L的最小值

    题解:

    如果(L+1)是n的约数,那么Bob必胜

    Alice取了x个石子,Bob取L+1-x个石子..

    发现按照这个策略取下去,Bob就赢了

    Bob总能使这一轮石子的数量减少t+1个

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 100010
     6 #define ll long long 
     7 #define ull unsigned long long 
     8 using namespace std;
     9 
    10 const int inf=0x3f3f3f3f;
    11 int n;
    12 int gint()
    13 {
    14     int ret=0,fh=1;char c=getchar();
    15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    17     return ret*fh;
    18 }
    19 
    20 int main()
    21 {
    22     scanf("%d",&n);
    23     int i,j,sq=(int)sqrt(n),ans=inf;
    24     if(n%2==0&&n/2>=3) ans=min(ans,n/2);
    25     for(i=3;i<=sq;i++) 
    26     {
    27         if(n%i==0)
    28         {
    29             ans=min(ans,i);
    30             if(n/i>=3) ans=min(ans,n/i);
    31         }
    32     }
    33     if(n>=3) ans=min(ans,n);
    34     printf("%d
    ",ans-1);
    35     return 0;
    36 }
    View Code

    POJ 1063 取石子游戏 (威佐夫博弈)

    题面:

    两堆石子,两个人轮流取石子,可以在一堆取任意数量或者在两堆取相同数量,取走最后一个石子的人胜利。问最后谁赢了

    题解:

    把问题搞到二维坐标系上,$x,y$分别对应两堆的石子个数

    显然$(0,0)$先手必败,把先手必败节点称为奇异节点

    发现奇异节点上下左右,以及右上和右下的点都不是奇异节点

    如果$Alice$不在奇异节点上,那么$Alice$可以通过一步操作到达奇异节点,然后$Bob$失败

    $(1,2)(3,5)$等等也是奇异节点

    经过奇异节点的3条直线上的点,都能通过一步到达奇异节点

    这不正是有向图的核的模型么

    怎么求答案呢

    需要用到$Beatty$定理

    $frac{1}{a}+frac{1}{b}=1$

    定义数列$A$:$left lfloor an ight floor$,数列$B$:$left lfloor bn ight floor$

    那么$A,B$就是整数的划分..证明不会

    打表发现$B_{i}-A_{i}=i$

    可得$b=a+1$,带入解得$a=frac{sqrt{5}+1}{2},b=frac{3-sqrt{5}}{2}$

    验证一下就行了

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 100010
     6 #define ll long long 
     7 #define ull unsigned long long 
     8 using namespace std;
     9 
    10 const int inf=0x3f3f3f3f;
    11 int n,m;
    12 
    13 int main()
    14 {
    15     int x,y,z;
    16     while(scanf("%d%d",&n,&m)!=EOF)
    17     {
    18         if(n>m) swap(n,m);
    19         if(n==0){ 
    20             if(m==0) puts("0"); else puts("1");
    21             continue; 
    22         }else if(n==1&&m==1){ puts("1"); continue; }
    23         y=m-n;
    24         x=(int)((sqrt(5.0)+1)/2*y);
    25         if(x==n) puts("0");
    26         else puts("1");
    27     }
    28     return 0;
    29 }
    View Code

    POJ 1063 Euclid's Game (多阶段博弈)

    题面:略

    题解:

    把问题转化成我们熟悉的模型,相当于有一排石子堆,必须把前面的石子堆取完了才能取后面的,取最后一个石子的人赢,问谁赢

    发现这次的游戏是分阶段进行的

    假设现在面对的石子堆中有x个石子

    如果$x=1$,必须取走这个石子,胜败由下一堆石子决定

    $xgeq 1$,可以自由转移到当前堆剩余棋子为1的状态,或者全取走进入下一轮游戏,显然必胜

    用位运算反着推一推就ok了

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 100
     6 #define M1 (N1<<1)
     7 #define ll long long 
     8 #define dd double
     9 #define idx(X) (X-'a')
    10 using namespace std;
    11 
    12 int gint()
    13 {
    14     int ret=0,fh=1; char c=getchar();
    15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    17     return ret*fh;
    18 }
    19 ll n,m; int d;
    20 int v[N1],f[N1];
    21 void gcd(ll x,ll y)
    22 {
    23     if(!y) return ;
    24     if(y>x) swap(x,y);
    25     v[++d]=x/y; gcd(y,x%y);
    26 }
    27 
    28 int main()
    29 {
    30     int i,j;
    31     while(scanf("%lld%lld",&n,&m))
    32     {
    33         if(n==0&&m==0) break;
    34         d=0; gcd(n,m);
    35         memset(f,0,sizeof(f)); f[d]=1;
    36         for(i=d-1;i>=1;i--)
    37             if(v[i]==1) f[i]=(f[i+1]^1);
    38             else if(v[i]>1) f[i]=(f[i+1]|1);
    39         if(f[1]) puts("Stan wins");
    40         else puts("Ollie wins");
    41     }
    42     return 0;
    43 }
    View Code

    POJ 1678 I Love this Game! (博弈DP+单调队列)

    题面:略

    题解:

    想了一个单调队列的做法,常数十分优秀,由于内存原因目前只排到了poj的rank3

    由于$a>0$,只能从小到大取。所以把$a_{i}$从大到小排序,模拟根据结果去推决策的过程

    有点类似于一双木棋那道题$max/min$博弈的方法

    定义$dp[i][0/1]$表示Alice/Bob取了$a_{i}$时答案的$max/min$

    由于是两个绝顶聪明在博弈,所以转移和其它的$DP$不同,我们要选取最劣决策..

    $dp[i][0]=min(dp[j][1]+a_{i}),dp[i][1]=max(dp[j][0]-a_{i}) $

    决策$j$的区间可以用单调队列维护

    细节比较多

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 10010
     6 #define ll long long 
     7 #define ull unsigned long long 
     8 using namespace std;
     9 
    10 const int inf=0x3f3f3f3f;
    11 int gint()
    12 {
    13     int ret=0,fh=1;char c=getchar();
    14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    16     return ret*fh;
    17 }
    18 
    19 int n,m,T,A,B,de;
    20 int q0[N1],q1[N1],hd0,hd1,tl0,tl1,a[N1],dp[N1][2];
    21 int cmp(int x,int y){ return x>y; }
    22 
    23 int main()
    24 {
    25     scanf("%d",&T);
    26     while(T--) {
    27         
    28     int i,j,ans=-inf;
    29     scanf("%d%d%d",&n,&A,&B);
    30     for(i=1;i<=n;i++) a[i]=gint();
    31     sort(a+1,a+n+1,cmp);
    32     memset(dp,0,sizeof(dp));
    33     hd0=hd1=1, tl0=tl1=0;
    34     //q0[++tl0]=0; q1[++tl1]=0;
    35     for(i=1,j=1;i<=n;i++)
    36     {
    37         if(a[i]==330)
    38             de=1;
    39         for(; j<i && a[j]-a[i]>B ;j++);
    40         for(; j<i && A<=a[j]-a[i] && a[j]-a[i]<=B ;j++)
    41         {
    42             while( hd0<=tl0 && dp[j][1]<=dp[q0[hd0]][1] ) tl0--;        
    43             q0[++tl0]=j;
    44             while( hd1<=tl1 && dp[j][0]>=dp[q1[hd1]][0] ) tl1--;
    45             q1[++tl1]=j;
    46         }
    47         while( hd0<=tl0 && a[q0[hd0]]-a[i]>B ) hd0++;
    48         while( hd1<=tl1 && a[q1[hd1]]-a[i]>B ) hd1++;
    49         if(hd0<=tl0) dp[i][0]=dp[q0[hd0]][1]+a[i]; else dp[i][0]=a[i];
    50         if(hd1<=tl1) dp[i][1]=dp[q1[hd1]][0]-a[i]; else dp[i][1]=-a[i];
    51     }
    52     for(i=1;i<=n;i++) if( A<=a[i] && a[i]<=B ) ans=max(ans,dp[i][0]);
    53     //for(i=1;i<=n;i++) printf("%d:%d %d
    ",a[i],dp[i][0],dp[i][1]);
    54     if(ans==-inf) ans=0;
    55     printf("%d
    ",ans);
    56         
    57     }
    58     return 0;
    59 }
    View Code
  • 相关阅读:
    1774:大逃杀
    Angular实现简单数据计算与删除
    IDEA 如何搭建maven 安装、下载、配置(图文)
    win10 Java JDK环境变量配置
    Nginx学习使用
    ASP.NET Core中返回 json 数据首字母大小写问题
    mysql使用遇到的问题
    线程同步以及AutoResetEvent
    Device Class
    Xamarin.Forms之布局压缩
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10389746.html
Copyright © 2011-2022 走看看