博弈论题目总结(二)——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,那么后手一定可以做出一种动作,保持Xor=0,那么先手必败。

反之某个状态的SG函数值不为0,先手可以让Xor=0,变成后手,重复上述动作,那么先手必胜

这样就能轻松合并多个独立的组合游戏啦

SG组合游戏

NIM游戏

有n堆石子,两个人玩游戏,每次轮流在一堆里取走任意个,取走最后一堆的最后一个石子的人赢,问谁赢

一堆石子相当于一个子游戏,显然该子游戏的SG函数值为该堆中石子数

再根据游戏的和的思想,把子游戏合并就能求出谁赢了

NIMk游戏

同样是NIM游戏,现在变成了每次在k堆中取任意个

NIM游戏采取策略的根本是,保证当SG函数值为0时,不论先手如何操作,后手一定能做出一种动作,保持Xor=0

把每堆石子都转化成k+1进制数,再进行不进位的加法即可

POJ有3道题目的题解待填坑..

SG组合游戏变形

一些SG组合游戏的终止状态比较特殊,我们可以通过转化解决

POJ 3537 Crosses and Crosses

题目大意:给出一个1*n的网格,一开始全都是白格子,两个人轮流把一个白格子涂黑,谁先涂出来连续3个黑格子谁就赢了

游戏的结束状态不容易直接搞啊

我们剖析游戏本身的性质

先手把一个格子涂黑后,它左右一共连续5个格子(边界另外讨论)一定不能被后手涂

相当于每次涂黑一个格子,删掉连续的不超过5个格子,两侧剩下的格子构成了一个或两个子游戏

依次求出长度为1~n的连续格子的游戏的SG函数即可

 1 #include <queue>
 2 #include <cmath>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define N1 2010
 8 #define M1 200010
 9 #define ll long long
10 #define dd double
11 using namespace std;
12 const dd eps=1e-7;
13
14 int n,tl;
15 int sg[N1],use[N1],que[N1];
16
17 int main()
18 {
19     scanf("%d",&n);
20     if(n==1){ puts("1"); return 0; }
21     if(n==2){ puts("2"); return 0; }
22     if(n==3){ puts("1"); return 0; }
23     if(n==4){ puts("1"); return 0; }
24     sg[0]=0; sg[1]=sg[2]=sg[3]=1; sg[4]=2;
25     int i,j;
26     for(i=5;i<=n;i++)
27     {
28         que[++tl]=sg[i-4]; que[++tl]=sg[i-3];
29         for(j=0;j+5<=i;j++) que[++tl]=sg[j]^sg[i-j-5];
30         for(j=1;j<=tl;j++) use[que[j]]=1;
31         for(j=0;j<=i;j++) if(!use[j]){ sg[i]=j; break; }
32         while(tl) use[que[tl--]]=0;
33     }
34     if(sg[n]>0) puts("1");
35     else puts("2");
36     return 0;
37 }

POJ 2311 Cutting Game

题目大意:给出一张n*m的纸,每次可以把它剪成两半,先剪出来1*1小纸片的人赢

虽然1*1是必败局面,但并不容易直接推出其他格子的SG函数

显然x>1时,1*x的局面先手必胜。而2*2局面必败,进而可以推出2*3,3*3也都是必败局面

利用这两点就可以轻松推出整张纸的SG函数了

 1 #include <queue>
 2 #include <cmath>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define N1 205
 8 #define M1 200010
 9 #define ll long long
10 #define dd double
11 using namespace std;
12 const dd eps=1e-7;
13
14 int T,n,m,de;
15 int sg[N1][N1],use[N1*2];
16
17 int main()
18 {
19     int i,j,k,x,y,ans,flag;
20     for(i=1;i<=200;i++) sg[1][i]=1;
21     for(i=1;i<=200;i++) sg[i][1]=1;
22     for(i=2;i<=200;i++) for(j=2;j<=200;j++) //if(i+j>4)
23     {
24         for(k=2;k<j-1;k++) use[sg[i][k]^sg[i][j-k]]=1; //if(i+k>=4&&i+j-k+1>=4)
25         for(k=2;k<i-1;k++) use[sg[k][j]^sg[i-k][j]]=1; //if(j+k>=4&&j+i-k+1>=4)
26
27         for(k=0;k<=200;k++) if(!use[k]){ sg[i][j]=k; break; }
28
29         for(k=2;k<j-1;k++) use[sg[i][k]^sg[i][j-k]]=0; //if(i+k>=4&&i+j-k+1>=4)
30         for(k=2;k<i-1;k++) use[sg[k][j]^sg[i-k][j]]=0; //if(j+k>=4&&j+i-k+1>=4)
31     }
32     while(scanf("%d%d",&n,&m)!=EOF)
33     {
34         if(sg[n][m]) puts("WIN");
35         else puts("LOSE");
36     }
37     return 0;
38 }

BZOJ 1457 棋盘游戏

题目大意:给出一个坐标系,上面有很多个皇后,皇后只能向左/向下/向左下走,两个人轮流每次选择一个皇后移动,谁先把皇后移动到(0,0)谁赢

如果直接把(0,0)当做游戏终止局面的话,求解的问题就是谁先把所有皇后都移动到(0,0)谁赢了

显然如果存在x=y||x=0||y=0的皇后,先手必胜

所以两个人都极力避免自已移动出来上述三种情况的皇后

而皇后只能向左下移动,最终皇后一定集中在(1,2)和(2,1)

我们把SG函数为0的位置设为(1,2)和(2,1)即可

 1 #include <queue>
 2 #include <cmath>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define N1 105
 8 #define M1 200010
 9 #define ll long long
10 #define dd double
11 using namespace std;
12 const dd eps=1e-7;
13
14 int T,n;
15 int sg[N1][N1],use[N1];
16
17 int main()
18 {
19     int i,j,k,x,y,ans,flag;
20     for(i=1;i<=100;i++) for(j=1;j<=100;j++) if(i!=j)
21     {
22         for(k=1;k<j;k++) if(k!=i) use[sg[i][k]]=1;
23         for(k=1;k<i;k++) if(k!=j) use[sg[k][j]]=1;
24         for(k=1;k<min(i,j);k++) use[sg[i-k][j-k]]=1;
25
26         for(k=0;k<200;k++) if(!use[k]){ sg[i][j]=k; break; }
27
28         for(k=1;k<j;k++) if(k!=i) use[sg[i][k]]=0;
29         for(k=1;k<i;k++) if(k!=j) use[sg[k][j]]=0;
30         for(k=1;k<min(i,j);k++) use[sg[i-k][j-k]]=0;
31     }
32     scanf("%d",&T);
33     while(T--)
34     {
35         scanf("%d",&n); ans=0,flag=0;
36         for(i=1;i<=n;i++)
37         {
38             scanf("%d%d",&x,&y);
39             if(x==y||!x||!y) flag=1;
40             ans^=sg[x][y];
41         }
42         if(ans||flag) puts("^o^");
43         else puts("T_T");
44     }
45     return 0;
46 }

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

时间: 2024-10-10 06:55:31

博弈论题目总结(二)——SG组合游戏及变形的相关文章

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

人类的本质是什么呢?复读机?鸽子? 博弈问题是很有意思的一类题目 我讲的可能不是很明白,但题目都不难建议自己思考 组合游戏的特点: 1.两个人博弈,轮流做出最优决策 2.玩家在每个时刻做出的决策都是能预测到的,是一个确定的集合 3.每种状态可能有多种方式到达,但同一种状态不能在一次游戏中重复到达,且没有平局的情况 4.只要能进行决策,就一定要决策,不能跳过这个回合 SG组合游戏 我们把每种状态抽象成一个点,在起点有一颗棋子,两个人选取最优策略轮流对这颗棋子进行移动,最后不能移动棋子的人失败 显然

组合游戏 - SG函数和SG定理

在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜点和必败点的概念: P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败. N点:必胜点,处于此情况下,双方操作均正确的情况下必胜. 必胜点和必败点的性质: 1.所有终结点是 必败点 P .(我们以此为基本前提进行推理,换句话说,我们以此为假设) 2.从任何必胜点N 操作,至少有一种方式可以进入必败点 P. 3.无论如何操作,必败点P 都只能进入 必胜点 N. 我们研究必胜点和必败点的目的时间为题进行简化,有助于

组合游戏(博弈)

昨天看大白书翻到了组合游戏这章,看着发觉原来是博弈论的内容,于是便看下去了.真是不看不知道,一看才知道自己的水平有多弱,不过好在还是集中精神地看了大部分.从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 11927 - Games Are Important(组合游戏+记忆化)

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

博弈-组合游戏

组合游戏: 规则1:一个状态是必败的状态,当且仅当它的所有后继状态为必胜状态 规则2:一个状态是必胜的状态,当且仅当它的所有后继状态中至少有一个是必败状态 1.Ferguson游戏: 两个盒子有石子n,m.游戏规则为选择其中一个盒子清空,把另一个盒子的石子拿一些给清空的盒子,但需保证至少都有一个..终态为[1,1] 状态: 从后向前推,[n,m]状态能推出的有一个为必败,则[n,m]必胜 [n,m]能推出的全为必胜,则[n,m]必败 #include <iostream> using name

组合游戏简单题

UVA - 11859 - Division Game 题目传送:Division Game AC代码: #include <map> #include <set> #include <list> #include <cmath> #include <deque> #include <queue> #include <stack> #include <bitset> #include <cctype>

组合游戏与博弈

[引言] 分金币问题:有100个金币,两个人轮流从这堆金币中取金币,规定每次至少取1个,最多取5个.取完最后一枚金币的人获胜.假入你先手取金币,对方是足够聪明的,你能找到一种必胜的方法吗? [问题分析] 首先我们考虑到这样一个简单的事实,如果最后把6个金币留给对方去取,那么很显然,对方必败.再往前推进一步,如果把12枚金币留给对方去取,对方也必败. 为什么呢?原因很简单,不论对方取几个,我都能够把金币取成6个,这样就回到上一个对方必败的局势了. 那么我们可以更进一步地推出,如果把金币取成18个,