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

Posted guapisolo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了博弈论题目总结——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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

以上是关于博弈论题目总结——SG组合游戏及变形的主要内容,如果未能解决你的问题,请参考以下文章

博弈论入门小结

POJ 3710 Christmas Game#经典图SG博弈

51nod_1714:B君的游戏(博弈 sg打表)

博弈论总结

Nowcoder 挑战赛23 B 游戏 ( NIM博弈SG函数打表 )

bzoj1022: [SHOI2008]小约翰的游戏John(博弈SG-nim游戏)