首页 > 其他分享 >浅谈有向无环图游戏

浅谈有向无环图游戏

时间:2024-03-12 23:44:17浏览次数:29  
标签:Macw 局面 浅谈 石子 环图 Position SG 节点 游戏

以前写的,一直没发。

浅谈有向无环图游戏

在做题的时候,往往能遇到一些有关博弈论的游戏…

公平组合游戏的解释

在一般计算机竞赛中,博弈论的题目通常以“公平组合游戏 Impartial Combinatorial Game”的题干呈现给选手。所谓的公平组合游戏,定义如下:

  1. 游戏有且仅有两个玩家,且游戏规则对每一位玩家都是绝对公平的。
  2. 两个玩家需要轮流对集合的元素进行操作,玩家需要在有限的合法操作中选择一个元素。
  3. 在游戏中,任何一种可能的情况被称之为局面Position。合法操作集合只取决于当前局面本身,而不取决于玩家。局面的改变被称之为移动Move。
  4. 若轮到某一名玩家时该玩家的操作集合为空($\emptyset$),则这名玩家被判定为游戏失败。

对于任意一种局面,有且仅存在两种情况。

  1. P-Position: 在当前局面下,先手必败。
  2. N- Position:在当前局面下,先手必胜。

局面有如下三种性质:

  1. 当合法操作集合为空时,当前局面被称之为P-Position,即定义第四条。
  2. 若某一种局面可以移动到P-Position,则称该局面一定为N-Position。
  3. 若所有移动都只能到达N-Position,则该局面一定为P-Position。

ICG题目的特点

在计算机考试中,ICG问题的题目中往往会让选手判断一个局面时N-Position还是P-Position(假设游戏的两位玩家都无比聪明,知道必胜的方法),这时我们就可以通过逻辑分析和逆向的推到来得到最终的结果。

根据ICG问题的定义,如果一个局面时N-Position的话,这位无比聪明的玩家一定会将局面改动到N-Position以至于让对手玩家陷入必败态。此时陷入必败态的玩家无论做什么合法操作,都只会让局面再次改动到N-Position。如此轮回下去。

因此,当两位玩家无比聪明时,游戏每一步的局面一定会是在N-Position和P-Position之间来回转移。

做ICG题目的常见套路

在做ICG题目时,常见的做题套路就是通过最终的必败态,即P-Position一步一步地向前推导,在必败态和必胜态之间来回转换,最终得出最终答案。

与此同时,做题者必须要找到一个必胜的技巧来保证“每一位玩家的决策”都是无比聪明的,不会误将原本有优势的局面(N-Position)转换成对对方有优势的局面(N-Position)。

练习题一:

假设原本有n个石子,Macw和Vincent轮流来取石子,每次每人只能取一个石子,拿到最后一个石子的人获胜(等同于最后一个没有石子的人失败)。假如Macw是先手,且双方都无比聪明,请你判断Macw是N-Position还是P-Position。

在看到题目后我们按照思路做一个简单的分析。假定[arr[i]]表示当初时石子个数为i的时候Macw是否可以赢得游戏。

根据ICG定义来看,当一开始有零个石子时,Macw必定会输,因为他已经没有了合法操作,因此arr[0] = 0。在知道一开始有零个石子时Macw必输,则可以反向推导出如果一开始有1个石子。则Macw必胜(虽然你不知道当前局面是否必胜,但你知道当前局面作出选择后就会让局面到达P-Position,因此这个局面是N-Position,如定义所述)。继续以此类推,最终可以总结出规律:

如果当前的棋子数量是奇数,则Macw必胜。若当前棋子的数量是偶数,则Macw必败。

练习题二:与第一题类似,但难度增加。

假设原本有n个石子,Macw和Vincent轮流来取石子,每次每人只能取4个石子,拿到最后一个石子的人获胜(等同于最后一个没有石子的人失败)。假如Macw是先手,且双方都无比聪明,请你判断Macw是N-Position还是P-Position。

依旧是根据题目定义创建一个arr数组,[arr[i]]表示初时石头的个数为i的时候Macw是否可以赢得游戏。

根据ICG游戏定义来看,arr[0] = 0表示如果没有石头则Macw必输。接下来就可以通过倒推的方式来得到结论了:当石子的个数为一个时,Macw可以一次拿完,则当前状态为必胜状态。以此类推,当石子的个数为1-4个时,Macw都可以一次拿完,因此arr[1-4] = 0。

当石子的个数为5个时,此时的Macw有四种选择:拿一个、拿两个、拿三个、拿四个。通过枚举Macw的每一种情况后可以发现不论哪几个之后局面一定是N-Position,所以当前局面一定是P-Position,则先手必败。

最后我们就可以用递推的方式来写代码了。

以下是递推版本的代码:

#include <iostream>
using namespace std;

int main(){
    int n;
    bool arr[10005] = {0, 1, 1, 1, 1};
    cin >> n;
    for (int i=5; i<=n; i++){
        int total = 0;
        // 遍历所有拿的情况。
        for (int j=1; j<=4; j++) total += arr[i-j];
        // 只能变成N-Position,则当前一定是P-Position。
        if (total == 4) arr[i] = 0;
        else arr[i] = 1; // 否则就是N-Position。
    }
    if (arr[n] == 0) cout << "Defeat" << endl;
    else cout << "Victory" << endl;
    return 0;
}

事实上,这种博弈类型的题目有一个自己的名字,即巴什博弈。关于巴什博弈还有一个更好的通解,原理与以上代码类似,即总结归纳:只有当arr[i]的前面四个数字都是1的时候,当前情况才是P-Position。因此可以得出结论是当初时的石头数量为(4+1),即5的时候,先手必败,否则先手必胜。

巴什博弈通解应用于上面那道题,当每个玩家每次取走的石头为m的时候,巴什博弈的通解如下:

#include <iostream>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;
    int position = n % (m + 1);
    if (position == 0) cout << "Defeat" << endl;
    else cout << "Victory" << endl;
    return 0;
}

有向无环图游戏 DG

任何情况下,一个公平组合游戏可以被一张或多张有向无环图表示出来。若一个ICG能被用有向无环图表示出来,则其可以被称之为有向无环图游戏。

例如上一个板块所提及的巴什博弈问题就是一个有向无环图游戏。具体地,我们用每一个顶点来表示每一种局面(Position),用每一条有向边<v1, v2>表示可以从v1这个局面操作变换到v2这个局面。拿巴什博弈m=3的时候可以画出的有向无环图如下:

可以看到,将巴什博弈转换成一个有向无环图后,可以很明显的看出这是一棵“递推树”或“递归树”的树形结构。这张有向无环图清晰的展示出了每一个局面上所有的合法操作集合。

在有向图上做游戏 SG函数

在看这一小节之前,请确保你完全理解了本文前半段所解释的所有概念,并且你需要有一定的前置高中数学知识才可以理解,否则你下面可能看不太懂。

先了解两个定义:

mex运算 (Minimum Exclusion)

$mex(S)\text{的结果为不属于集合S中的最小的非负整数。}$
$mex(S) = min{x}(x \in N, x \notin S)$

例如:$mex({1, 2, 3}) = 0, mex({0, 1, 2}) = 3, mex({0, 1, 3}) = 2$。

SG函数

设状态(节点)x有k个后继状态(子节点)$y_1, y_2, y_3, ..., y_k$。
$SG(x) = mex({SG(y_1), SG(y_2), SG(y_3), ..., SG(y_k)})$

若x后面没有任何的后继节点,则$SG(x) = 0$。

知道了mex运算和SG函数后有什么用呢?

在一个有向图中(如巴什博弈m=3那张图),若一个节点没有后继节点则代表着个节点一定是一个必败态P-Position。SG函数的作用就是判断一个节点的后继节点是否有P-Position,如果该节点的后继节点有任意一个P-Position,则该节点一定是一个N-Position。(只要可以到达P-Position的局面都是N-Position)。

为什么呢?

因为SG函数的作用就是找到其所有后继节点并对这些节点统一作集合的mex运算。换句话说,如果在图上某一个顶点的SG值为k的话,则这个节点必然可以到达所有局面小于k的局面。如果一个点的SG函数的值为6,则代表其至少有6个子节点,并且者这六个子节点的SG值分别是{0, 1, 2, 3, 4, 5},则玩家只需要选中SG值为0的节点就可以达到必胜条件N-Position。

总结:所有可以到达SG(x)=0的节点必定是P-Position,玩家一定可以将当前的P-Position局面扭转到N-Position局面

有向无环图游戏的常见题目如下:

给定一个有n个节点和m条边的有向无环图,k个棋子所在的节点编号。两名玩家交替移动棋子,每次只能将任意一颗棋子沿有向边移到另一个点,无法移动者视为失败。如果两人都采用最优策略,问先手是否必胜。

这道题就可以通过SG函数轻松的求解。本题的必胜技巧就是判断起始节点的SG值,如果起始节点的SG函数值大于0,则该节点一定可以到达SG节点为0的点。如果SG函数的值为0,则对方选手只能将棋子转移到SG节点为正整数的节点上,则在第三回合中,先手就可以再次的将棋子挪动到SG函数值为0的节点上。如此往复循环,最终让后手在最后关头无法进行合法操作,先手制胜。

因此本题的关键点就在于:如果棋子初始节点的SG值为0,则先手必胜,否则先手必败。(具体可以好好想想,确实很难理解)。

Nim游戏

甲,乙两个人玩 nim 取石子游戏。

nim 游戏的规则是这样的:地上有 $n$ 堆石子(每堆石子数量小于 $10^4$),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 $n$ 堆石子的数量,他想知道是否存在先手必胜的策略。

这道题就是经典的Nim博弈问题,Nim博弈问题在博弈论中是属于一个典型的考点,因此学习Nim博弈是极其有必要的。在学习SG函数之后,就可以很轻松的解决本题了。先看一下样例为1,1,2的时候博弈图(有向无环图DG)的样子:

【这看起来非常头疼,我做起来也很头疼】这题的暴力思路就是枚举出每一种情况,然后自底向上逆向推导,推导出每一个节点的SG值,最后判断初始节点的SG值即可。


备注:部分定义内容来源于百度或万维网,解释性内容均为本人自己撰写的。

标签:Macw,局面,浅谈,石子,环图,Position,SG,节点,游戏
From: https://www.cnblogs.com/Macw07/p/18069658

相关文章

  • 【C语言】用C语言写一个猜数字游戏
    目录1.游戏规则2.实现逻辑3.代码展示4.结果展示1.游戏规则1.游戏开始,电脑会从1-100随机生成一个数2.玩家在屏幕上输入猜的数字3.电脑根据这两个数比较大小;若是猜大了,屏幕显示猜大了,若是猜小了,屏幕显示猜小了。4.循环上面的操作,直至猜正确。2.实现逻辑1.首先......
  • 浅谈非内存对抗类和AI自瞄类FPS作弊程序原理及常用反反作弊措施与反作弊应对手段(上)
    一、引言    闲来无事,在浏览微信公众号的时候无意刷到了江西余江警方关于破获全国首例“AI自瞄”类外挂的案件,涉案金额达到惊人的3000余万。不得不感叹近年来AI相关科技发展之迅速及国内有关于FPS类及其他大类游戏作弊的黑产市场之大。    在工作学习之余,......
  • GDPU unity游戏开发 滚动小球
      解锁你的游戏大门,适合小白入门看的,通过简单的实例大概了解unity的一些基本操作。常用快捷键 CtrlC/V/X/Z对应复制/粘贴/剪切/回退很多小白都惟手熟尔了,W物体对象的位置/平移/移动 ,E物体对象旋转,R物体对象缩放,Q/Alt中键用于场景的移动,右键/Alt左键用于场景的旋转,滚......
  • abc221D 统计在线游戏人数
    有一款在线游戏,共有n名注册玩家,第i名玩家从第a[i]天开始上线,持续在线b[i]天。现在要统计在线人数为x[i]的天数,其中1<=x[i]<=n。1<=n<=2E5;1<=a[i],b[i]<=1E9;先按差分更新,然后求前缀和还原,再遍历一次统计答案,时间复杂度O(n)。#include<bits/stdc++.h>usingnamespacestd;#......
  • 从keys命令出发-浅谈redis的字典和字典迭代器
    1.keys命令keys命令相信大家应该都用过,该命令会遍历整个redis的字典空间,对要查找的key进行匹配并返回。就像官方文档所说:在生产环境使用该方法的过程中要非常小心,因为redis服务器在执行该命令的时候其他客户端读写命令都会被阻塞。使用方法:KEYSpattern示例:127.0.0.1:6379......
  • 45. 跳跃游戏 IIc
    暴力DFS超时了。先放着把。intmin;voiddfs(int*nums,intnumsSize,intindex,intcount){if(index>=numsSize-1){if(count<min)min=count;return;}for(inti=nums[index];i>0;i--){dfs(nums,numsSize,index+i,count+1);......
  • 55. 跳跃游戏c
    动态规划实在让人上瘾啊,虽然过程很难想,但是代码实现实在太简单了。intmax(inti,intj){if(i>j)returni;returnj;}boolcanJump(int*nums,intnumsSize){if(numsSize==1&&nums[0]>=0)returntrue;if(nums[0]==0)returnfalse;int*dp=(......
  • 【游戏设计随笔06】关于《塞尔达传说》的迷宫设计(dungeons design)的一些思考
    在塞尔达里,迷宫是多个小房间的组合,有些锁着的小房间是需要“小钥匙”这一道具去解锁才能通行的。关卡设计问题的出现:初代的塞尔达中,钥匙可以在整部游戏的任何门上使用,这导致了各种麻烦的情况。通常你持有的钥匙是大于需要解锁的房间的,因为随着游戏进程的推进,一些需要解......
  • 试着写一下MMORPG游戏游戏的自动挂机
    因为,视频里教到了植物大战僵尸的自动放置Call就结束了,所以暂且先跟着视频走。而视频就开始研究mmorpg游戏了。所以我打算跟着视频走。而上个项目大体能够理解其实就是用CE找基址,然后通过代码注入的方式实行自动脚本之类的东东。至于CE找基址OD找call这些设计经验的东西我会慢慢......
  • CTS2024 投票游戏
    首先手玩可以发现求出两人谁先被票出是困难的,但如果我们能求出两人各票出时的票数,那么只要比较一下票数的大小就可以直到票出的顺序,然而一个点的票数的大小与其子结点有关,如果我们能确定子结点最终票出时的票数,那么只要处理当且菊花图的一个问题即可,将子节点的最终票数从大到小排......