在算法竞赛中,博弈论题目往往是以icg。通俗的说就是两人交替操作,每步都各自合法,合法性与选手无关,只与游戏有关。往往我们需要求解在某一个游戏或几个游戏中的某个状态下,先手或后手谁会胜利的问题。就比如经典的:几堆石子,两人可以分别拿若干个,一次只能选择一个石子堆操作,问给定状态下,先手胜利还是后手胜利?

  而nim与sg函数就是对于这类问题的解法,在我的理解看来,sg函数和nim分别对应不同阶段的决策:前者对于单个游戏决策,后着是将这些单个游戏综合起来的整体决策

一、状态与转移

  icg游戏往往可以分为两个部分,规则与局面。而这两个分别对应了转移与状态。规则决定了状态转移的合法性,状态是转移的基本。

  什么是状态?状态是一个静态的局面。就好比当下棋时的局面一样。在游戏的每个阶段,不论是开始,中间,或是结束。每一个局面都对应了一种状态。

  什么是状态的转移?单个分开的局面无法构成一个完整的游戏,所以就需要从某一个状态转移到另一个状态,来进行一次操作。

  举个例子:有5个石子放在一堆。

        5个石子就是一种状态,在不受限制下,你可以改变这个状态。

        例如:取走4个石子。就是将5个石子这个状态转移到1个石子这个状态,操作就是取走4个石子。

        而这个操作的合法性取决于游戏的规则。

        例如:一次最多取3个石子。 那么上条操作取4个石子就是一次不合法的操作,意味着你不能从5这个状态直接转移到1这个状态。

      那么对于5个石子,每次最多取三个,从中我们可以得到如下状态转移的图(有向)

          

二、sg函数

   概念

   首先,引入mex值的概念。mex是指不属于集合中整数的最小正整数。而sg值就是不属于后继集合的最小正整数。

   例如上图中:0没有后继元素  所以最小正整数为0,sg(0)=0;

         1后继元素只有0,不属于后继集合的最小正整数为1,sg(1)=1;

         同理可得sg(2)=2;sg(3)=3;

         到4的时候,情况就发生了变化。由于4不能直接转移到1,故后继集合只有{1,2,3},sg(4)=0;

   这里的状态用1,2,3,4之类是为了举例,而实际上状态不一定是这样转换,可能有很多种状态,不仅仅局限于单个数字,亦可以是某种局面,某个棋盘局面,某个路径局面,如果能找到”状态“只要这个游戏没有循环,在有限步数可以达到结果,就可以对这个游戏的每个状态求出sg值。

   求解

  求解顺序是这样的。首先找到所有的结尾局面。比如取石子游戏中的石子剩余为0,或是棋盘游戏中棋子的无路可走,所以这些状态的sg值都为0,然后从这个状态逆向求他的前驱,求出所有前驱的sg值。 如果了解过拓扑排序,那么很容易理解这个过程。用拓扑排序来讲就是一个状态的后继sg值没有确定时,这个状态的sg值也无法确定。故从所有无路可走的局面按照逆向的拓扑排序过程就可以求出所有可达状态的sg值。

   意义

  求出了sg值后,对于解这个游戏胜负关系有什么用呢?

  由上面的概念我们可以得到两个结论

  1.  sg值为0的状态,前一状态一定全是非0状态。(一定全是非0状态)
  2. sg值为非零的状态,一定能一步转移到0状态(不一定必须转到,是可以转到)

  由此我们可以进行决策。

  如果我们先手是非零,我们可以始终选择一步转移到sg值为0的状态,那么下一家一定只能转移到非0。那么到你又可以一步转移到0。循环如此决策,就可以让下一家最终转移到败态的0状态处,从而获得胜利

  如果我们先手是零,那么我们只能转移到非0状态,下一家可以用以上必胜决策进行操作,那么我们就必败。

  由此我们得到sg值与胜利与失败的关系。

  sg(此状态)=0时,先手的人必败。即(此状态)为必败态

  sg(此状态)≠0时,先手的人必胜。即(此状态)为必败态

  

  sg函数可以看做在这个游戏下规则的体现,可以直接反映出转移的方式。而sg()函数某个值可以视作某个状态下的胜负情况。

  往往一个游戏需要求的只是一个局面下的胜负情况。即一个sg(a),但实际运用中需要通过求出每个中间态的sg值来推出所需状态的sg值。是不是有点动态规划的思想?

三、nim博弈

  然而,一个游戏可能由多个状态共同构成,即两个状态间不能互相影响或转移到同一个末状态。这时单纯的sg函数就不够解题了。因此我们引入了一个多游戏间的决策,nim博弈。对于多个游戏间的博弈,不能用简单的sg函数表达。可以把这个复合的游戏进行转变,成为多个互不影响的游戏,即每个游戏可以各自求出各自的sg函数,解出各自状态对应的sg值。将多个游戏+状态的sg值综合起来的方式,即为nim。

  求解方式是res=sg[1]^sg[2]^sg[3]^...^sg[n]。即为这些游戏组合在一起后,整体的胜负态。

  右方sg值对应的是那个游戏的起始状态的sg值。

  (sg补充)sg值不用单纯的0和非0来表示的原因:

     多个游戏中,比如两个游戏,一个是必胜态,一个是必败态。如果按照单个游戏都要必胜的策略玩在一个游戏结束时,再玩下一个游戏,相当于先后手对调,先手必败。但是先手可以将某一个的状态从非零依旧转移到非零,从而改变整体胜负态,比如从sg=2转移到sg=1,对手无法再扭转回来,自己就可以获胜。但是如果sg更大,那么双方会持续做这个抉择。这个抉择是有尽头的,到这个尽头时的状态就决定了最后整体胜负态,这个决策可执行的次数就是sg值的数量,比如sg=5时,最多可以多转移4次(然而转移四次不是一定最佳选择,读者可以进行模拟思考) 因此sg值要取具体值,在异或的时候各自的信息也提供了用处。

  nim的决策公式推出的多个游戏组合后的值,就对应了整体的胜负态。

四、例题

  HDU-1524 A-chess-game

  两者综合运用多个棋子分别看待,对应两个独立的游戏,拓扑排序,然后求出每个局面打表求出对应的sg值。

  由于在同一套规则上,所以可以用同一个sg函数。将所有起始局面的sg值异或起来,根据是否为0求出结果

  代码如下

  

 1 #include<iostream>
 2 #include<vector>
 3 #include<cstring>
 4 using namespace std;
 5
 6 struct point{
 7     int sg=0;
 8     vector <int> out;
 9     vector <int> in;
10     int inn=0;
11     int sgg=0;
12 }graph[10005];
13 bool num[100005];
14 int mex(int x)
15 {
16     memset(num,0,sizeof(num));
17     for(int i=0;i<graph[x].in.size();i++){
18         num[graph[graph[x].in[i]].sg]=1;
19     }
20     for(int i=0;i<10005;i++){
21         if(num[i]==0)return i;
22     }
23 }
24 void get_sg(int n)
25 {
26     for(int i=0;i<n;i++){
27         for(int i=0;i<n;i++){
28             if(graph[i].inn==0&&graph[i].sgg==0){
29                 graph[i].sg=mex(i);
30                 graph[i].sgg=1;
31                 for(int j=0;j<graph[i].out.size();j++){
32                     graph[graph[i].out[j]].inn--;
33                 }
34             }
35         }
36     }
37 }
38 int main()
39 {
40     int n,m,a;
41     while(cin>>n){
42         for(int i=0;i<=n;i++){
43             graph[i].out.clear();
44             graph[i].in.clear();
45             graph[i].sg=0;
46             graph[i].inn=0;
47             graph[i].sgg=0;
48         }
49         for(int i=0;i<n;i++){
50             cin>>m;
51             for(int j=0;j<m;j++){
52                 cin>>a;
53                 graph[i].in.push_back(a);
54                 graph[a].out.push_back(i);
55                 graph[i].inn++;
56             }
57         }
58         get_sg(n);
59         int res=0;
60         while(cin>>m){
61             if(m==0)break;
62             res=0;
63             for(int i=0;i<m;i++){
64                 cin>>a;
65                 res^=graph[a].sg;
66             }
67             if(res!=0)cout<<"WIN"<<endl;
68             else cout<<"LOSE"<<endl;
69         }
70     }
71 }

hdu 1524

  POJ-2960 S-Nim

  从0开始,对逆向根据规则对每个状态打表,求出sg值(如果某个点求过了,就不要重复求),再根据nim将多个结果异或起来

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int sg[10005];
 5 int s[10005];
 6 bool num[10005];
 7 int n,m;
 8 int mex(int x){
 9     memset(num,0,sizeof(num));
10     for(int i=0;i<n;i++){
11         if(x-s[i]>=0){
12             num[sg[x-s[i]]]=1;
13         }
14     }
15     for(int i=0;i<10005;i++){
16         if(num[i]==0)return i;
17     }
18     return -1;
19 }
20 int get_sg(){
21     for(int i=0;i<10005;i++){
22         sg[i]=mex(i);
23     }
24 }
25 int main()
26 {
27     int T ;
28     int p,q;
29     while (cin >> n) {
30         memset(sg,0,sizeof(sg));
31         if (n == 0) break;
32         for (int i = 0; i < n; i ++)cin >> s[i];
33         get_sg();
34         cin >> m;
35
36         while (m--){
37             int res = 0;
38             cin>>q;
39             for(int i=0;i<q;i++){
40                 cin >> p;
41                 res ^= sg[p];
42             }
43             if(res==0)cout<<"L";
44             else cout<<"W";
45         }
46         cout<<endl;
47     }
48     return 0;
49 }

poj 2960

  

如有疑问或异议,欢迎私信博主进行讨论与交流

  

转载于:https://www.cnblogs.com/greenpepper/p/11235232.html

博弈论基础之sg函数与nim相关推荐

  1. 博弈论与SG函数(Nim游戏)

    博弈论与SG函数(Nim游戏) 目录 博弈论与SG函数(Nim游戏) 游戏状态 状态图(SG图) Nim 游戏 Nim 和 SG函数 Grundy数字 组合博弈游戏 Grundy 游戏 例题 在本篇, ...

  2. 【博弈论】博弈论入门笔记(四类基础博弈+SG函数)

    『博弈论定义』 博弈论又被称为对策论(Game Theory):是二人或多人在平等的对局中各自利用对方的策略变换自己的对抗策略,达到取胜目标的理论.博弈论是研究互动决策的理论.博弈可以分析自己与对手的 ...

  3. 闲来无事刷水题、简单博弈论专题、sg函数、洛谷

    记 今天闲来无事,不想刷codeforces了,到洛谷提高组训练营找几道水题刷着玩玩(虽然自己早已过了打OI的年纪)- 简单博弈论专题 P1199 三国游戏 这么考虑,由于电脑总是不能让我搭配出当前能 ...

  4. 博弈论初步(SG函数)

    讲解见此博客https://blog.csdn.net/strangedbly/article/details/51137432 理解Nim博弈,基于Nim博弈理解SG函数的含义和作用. 学习求解SG ...

  5. sg函数和nim问题

    "Sprague-Grundy函数" 我们将面对更多与Nim游戏有关的变种,还会看到Nim游戏的a1^a2^...^an这个值更广泛的意义. 上面的文章里我们仔细研究了Nim游戏, ...

  6. SG函数求解 NIM游戏先手必胜必败问题

    NIM游戏: 两人玩家,给定状态之间转移规则,每个人轮流移动,最终得出胜负. 设P为必败点,N为必胜点 精髓: 能到达必败点的所有点都为必胜点,一般解题都是先找到一个必败点,然后由此点推必胜点 有的题 ...

  7. 剪纸游戏(博弈论)(SG函数)

    文章目录 题目描述 解析 题目描述 解析 本题的关键就是SG函数的定义 尝试了一些自己直观上可能对但题解没有使用的约定方法(当然最后证明都是错的 ...),对SG的理解更深刻了一些 SG=0的含义是无 ...

  8. 【博弈论】【SG函数】bzoj1777 [Usaco2010 Hol]rocks 石头木头

    仅有距根节点为奇数距离的节点的石子被移走对答案有贡献,∵即使偶数的石子被移走,迟早会被再移到奇数,而奇数被移走后,不一定能够在移到偶数(到根了). 最多移L个:石子数模(L+1),比较显然,也可以自己 ...

  9. 【博弈论】【SG函数】bzoj1457 棋盘游戏

    一开始就必胜的特判一下. #include<cstdio> #include<cstring> #include<set> #include<algorith ...

  10. 【acm 博弈论 】 之 Nim游戏与sg函数

    文章目录 前言 巴什博弈 威佐夫博弈 Nim游戏 Nim游戏与sg函数 题目 题意 样例 思路 代码 前言 从今天开始复习和整理下acm的部分模块,从博弈论开始. 著名的"取石子" ...

最新文章

  1. 计算机管理器中没有停止共享,域客户端默认共享关闭讨论.
  2. JVM 的内存模型及jstat命令的使用
  3. 千万千万不要运行的 Linux 命令
  4. linux sd卡 u盘区别,linux下实现U盘和sd卡的自动挂载
  5. Spring全自动AOP和项目加入jar包
  6. win7系统锁定计算机设置方法
  7. JDK 11 将引入低延迟 GC,大幅度缩短 GC 暂停时长
  8. 你还不知道web自动化测试是什么吧?今天让你发现新大陆
  9. qt 取消按钮点击效果_Qt 对话框里添加确定取消按钮
  10. 产品读书《终结拖延症》
  11. java 10000以内的质数_10000以内的质数表.doc
  12. 固态U盘能装linux么,用u盘在固态硬盘上安装linux 多重开启系统
  13. 怎么理解“付费搜索广告应当与自然搜索结果明显区分”
  14. 将HTML5封装成android应用APK文件的几种方法
  15. fflush函数作用浅析
  16. 现场总线过程控制系统实验装置
  17. Substrate之旅1:Polkadot是什么
  18. RabbitMQ实现死信队列
  19. MySQL是如何保证主从一致的
  20. Linux C小项目 —— 简单的web服务器

热门文章

  1. Java8 实战系列-01-序章
  2. 图像型PDF如何免费转换成可编辑的文字
  3. qt5以后正常显示中文(windows下 vs编译器)
  4. Oracle数据库SQL优化详解
  5. 将shp数据导入SQL Server
  6. 蓝桥杯题目练习 基础篇 [蓝桥杯2015初赛]奖券数目
  7. 使用arcgis进行夜间灯光数据处理
  8. 珞珈一号夜间灯光数据评价
  9. android9.0官方下载,安卓9.0系统安装包下载
  10. 山寨版学子商城——成功上线!