码迷,mamicode.com
首页 > 其他好文 > 详细

博弈论题目总结(一)——组合游戏

时间:2019-02-17 00:49:54      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:getc   poj   等等   math   维护   through   put   code   重复   

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

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

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

 

组合游戏的特点:

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\n",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 \right \rfloor$,数列$B$:$\left \lfloor bn \right \rfloor$

那么$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$,必须取走这个石子,胜败由下一堆石子决定

$x\geq 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\n",a[i],dp[i][0],dp[i][1]);
54     if(ans==-inf) ans=0;
55     printf("%d\n",ans);
56         
57     }
58     return 0;
59 }
View Code

 

博弈论题目总结(一)——组合游戏

标签:getc   poj   等等   math   维护   through   put   code   重复   

原文地址:https://www.cnblogs.com/guapisolo/p/10389746.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!