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

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

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

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

组合游戏的特点:

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 }

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 }

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 }

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 }

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

时间: 2024-10-14 01:33:50

博弈论题目总结(一)——组合游戏的相关文章

博弈论题目总结(二)——SG组合游戏及变形

SG函数 为了更一般化博弈问题,我们引入SG函数 SG函数有如下性质: 1.如果某个状态SG函数值为0,则它后继的每个状态SG函数值都不为0 2.如果某个状态SG函数值不为0,则它至少存在一个后继的状态SG函数值为0 如果某个局面SG函数值为0,则该局面先手必败 放到有向图中,该有向图的核就是SG值为0的点构成的集合 游戏的和 游戏的和的SG函数值=所有子游戏SG函数值的异或和Xor 如果所有子游戏都进行完毕,那么Xor=0,必败 如果某个状态的SG函数值为0,那么后手一定可以做出一种动作,保持

组合游戏(博弈)

昨天看大白书翻到了组合游戏这章,看着发觉原来是博弈论的内容,于是便看下去了.真是不看不知道,一看才知道自己的水平有多弱,不过好在还是集中精神地看了大部分.从Nim游戏(n堆石子,每人每次可以从任意一堆中取至少1个.至多整堆的石子,不能取者为输)开始讲起,引入必胜态.必败态的概念—— 1. 一个状态是必败状态当且仅当它的所有后继都是必胜状态. 2. 一个状态是必胜状态当且仅当它至少有一个后继是必败状态. 这是刘汝佳大神说的,说得通俗一点就是,必败态的所有后继都是必胜态,必胜态只需有一个后继是必败态

uva 12163 - Addition-Subtraction Game(组合游戏)

题目链接:uva 12163 - Addition-Subtraction Game 题目大意:两个人进行游戏,对于每一局有一个无向图,给出无向图,每个节点有个K值,两人轮流操作,每次可以选中国一个含有石子的节点,将该节点的一个石子拿掉,然后选择K个有边连接的节点加上一个石子(节点可以重复选择),每个节点的子节点不会超过15个.不能操作的人视为失败.每局有n轮,给定每轮中每个节点上石子的初始值,问先手胜利还是失败. 解题思路:有向图上移动石子的组合游戏,对于没有子节点的节点SG值为0,然后对于每

uva 1378 - A Funny Stone Game(组合游戏)

题目链接:uva 1378 - A Funny Stone Game 题目大意:两个人玩游戏,对于一个序列,轮流操作,每次选中序列中的i,j,k三个位置要求i<j≤k,然后arr[i]减1,相应的arr[j]和arr[k]加1,不能操作的人输,问先手是否必胜,必胜的话给出字典序最下的必胜方案,负责输出-1. 解题思路:首先预处理出各个位置上的SG值,然后对于给定序列,枚举位置转移状态后判断是否为必败态即可. #include <cstdio> #include <cstring&g

uva 11249 - Game(组合游戏)

题目链接:uva 11249 - Game 题目大意:给定K和N,表示有N轮游戏,每轮游戏给定两堆石子的个数,两人轮流操作,每次操作可以选择一堆取任意数量的石子,也可以选两堆取,要求两堆取的石子数之差的绝对值小于K,不能操作者为输,问先手的胜负情况. 解题思路:傻逼先手才一次取完,那样的话对手直接将另一堆取光不就傻逼了.所以先手就有一个取石子的最优策略,当两堆石子的数量差小于等K的时候,先手可以一次性取完所有的. 我们设f(x)为一堆石子的数量为x时的必败态,即x,f(x),为先手必败态,x<f

uva 12293 - Box Game(组合游戏)

题目链接:uva 12293 - Box Game 题目大意:有两个盒子,第一个盒子装有n个球,第二个盒子装又1个球,每次操作将少的盒子中的球全部拿掉,并从另一个盒子中取一些球放入该盒子,不能使另一个盒子中球的个数为0.两人轮流操作,问说最后谁胜. 解题思路:n如果为2i?1那么先手必败. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; bool judge (

uva 11927 - Games Are Important(组合游戏+记忆化)

题目链接:uva 11927 - Games Are Important 题目大意:给出一张无环有向图,并给出每个节点上的石子数,每次操作可以选择一个石子,向下一个节点移动.两人轮流操作,直到不能操作为失败者. 解题思路:有了图之后,用记忆化的方式处理出每个节点的SG值,取所有石子数为奇数的节点的Nim和. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; con

博弈论 题目整理

博弈论里面一个非常重要的结论: 如果前一个状态所有可能都是必败态,那么当前状态一定是必胜态. 如果前一个状态所有可能有一个是必胜态,那么当前状态一定是必败态. POJ 2484 A Funny Game 博弈游戏里面后手经常占据优势.除了A可以一次性全部拿光的情况,其他时候B都可以采取与A相同的策略,这样每次将石子分为相同的两组,最后获胜的一定是B. #include <cstdio> #include <cstring> #include <algorithm> us

POJ 2348 Euclid&#39;s Game 组合游戏

题目大意:有两个人玩游戏,有两堆石子,每次一个人要从其中一堆石子中拿走一些石子,当出现有一对石子变成0的时候这个人就输了,另一个人就赢了.给出初始石子有多少,问谁能赢. 思路:基础的组合游戏的判定问题,这个题没有给数据范围,非常的坑爹,据说需要long long. 第一次做组合游戏的题目,想想还有些小激动呢.昨天听同学讲了讲,我来现学现卖一下: 由于组合游戏的公平性,那么:如果一个状态的子状态会使先手必败,那么当前状态的先手必胜: 如果不存在一个子状态会使先手必败,那么当前状态先手必败. 利用这