算法基础之数学知识——公平组合游戏ICG

编程入门 行业动态 更新时间:2024-10-27 02:21:48

算法基础之数学知识——公平<a href=https://www.elefans.com/category/jswz/34/1769978.html style=组合游戏ICG"/>

算法基础之数学知识——公平组合游戏ICG

概念:

公平组合游戏ICG
若一个游戏满足:

1.由两名玩家交替行动;

2.在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;

3.不能行动的玩家判负;

则称该游戏为一个公平组合游戏。
NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。

介绍:

1.Mex运算

设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即:

mex(S) = min{x}, x属于自然数,且x不属于S

2.SG函数

在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1, y2, …, yk,定义SG(x)为x的后继节点y1, y2, …, yk 的SG函数值构成的集合再执行mex(S)运算的结果,即:

SG(x) = mex({SG(y1), SG(y2), …, SG(yk)})
特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(s)。

经典Nim游戏也是公平组合游戏,每一堆石子算一个有向图,这个有向图的起点就是每一堆石子初始的数量,假设一堆石子数量为3个,那么说明这个有向图起点就是3,又因为经典Nim游戏中可以拿1——3(这一堆石子所有的数量),所以我们画出这个有向图为下图:

图中的点3就是这个有向图的SG值。 因为点3可以拿1,2,3个石子,所以3号点可以连接到0,1,2这三个点,然后2号点可以拿1,2两个数量的石子,所以2号点可以连接0,1这两个点。然后计算出3号点的SG函数值为3,所以这个有向图的SG函数值为3
总的来说,有向图的建立就是当前状态能转移到哪种状态,那么它就可以连接那些状态的点。并且如果出边为0的话SG函数值就是0.

定理:

设G1, G2, …, Gm 是m个有向图游戏。定义有向图游戏G,它的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步。G被称为有向图游戏G1, G2, …, Gm的和。
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数值的异或和,即:

SG(G) = SG(G1) ^ SG(G2) ^ … ^ SG(Gm)

有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0。

有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。

上面说了,经典Nim游戏是一个有向图游戏,并且可以得出每一堆石子对应的有向图的SG函数就是这堆石子的数量(因为可以任意拿这堆石子),所以有向图要想先手必赢的话就得让每一堆石子的数量异或和不为0。也就是所有有向图的SG函数异或和不为0。

例题:
给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S。

现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式
第一行包含整数 k,表示数字集合 S 中数字的个数。

第二行包含 k 个整数,其中第 i 个整数表示数字集合 S 中的第 i 个数 si。

第三行包含整数 n。

第四行包含 n 个整数,其中第 i 个整数表示第 i 堆石子的数量 hi。

输出格式
如果先手方必胜,则输出 Yes。

否则,输出 No。

数据范围
1≤n,k≤100,
1≤si,hi≤10000

输入样例:
2
2 5
3
2 4 7

输出样例:
Yes

思想:
由上面的说明我们可以得出这是一个公平组合游戏,所以我们可以将这个游戏变成一个有向图游戏。但是每一堆只能拿集合里面数量的游戏,所以我们按照要求做出有向图。例如某一堆有10个石子,集合里面的元素为2,5,那么我们就可以从这堆石子里面每次拿2个或者5个。有向图如下:

所以这个有向图的SG为0。每一个堆都按照这种方法得出SG,然后将之异或起来就可以得出结果。

代码如下:

#include<iostream>
#include<cstring>
#include<unordered_set>using namespace std;int k, n, res;
//f数组保存一下SG值,减少重复计算 ,a数组保存的是集合里面的元素
int f[10005], a[105];int sg(int x)
{//如果已经求过x的sg值了,那么就会保存在f函数里面,这时候就可以直接返回了if(f[x] != -1)    return f[x];//unordered_set保存的是当前节点的后继节点的SG值,这样方便计算当前节点的SG值unordered_set<int> m;for(int i = 0; i < k; i ++){//如果当前的石子数量大于集合中某些元素,那么就说明可以拿这些元素数量的石子。//也就说明当前节点的后继节点就是x - a[i]if(x - a[i] >= 0){//求得后继节点的SG值,并存储至set//注意,此时当前节点的set不包括后继节点的set。m.insert(sg(x - a[i]));}}//计算当前节点的SG值for(int i = 0; ; i ++){if(!m.count(i)){return f[x] = i;}}
}int main()
{cin >> k;//输入集合a,存储每一次可以拿多少石子for(int i = 0; i < k; i ++){cin >> a[i];}cin >> n;//将f数组初始化为-1memset(f, -1, sizeof f);for(int i = 1; i <= n; i ++){int x;cin >> x;//计算每一堆的sg函数并且将之异或起来res ^= sg(x);}if(res) cout << "Yes";else    cout << "No";
}

更多推荐

算法基础之数学知识——公平组合游戏ICG

本文发布于:2024-02-26 09:46:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1702071.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:组合   算法   数学知识   公平   基础

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!