集合-Nim游戏

集合-Nim游戏

给定n堆石子以及一个由k个不同正整数构成的数字集合S。
现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合S,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数k,表示数字集合S中数字的个数。
第二行包含k个整数,其中第i个整数表示数字集合S中的第i个数si。
第三行包含整数n。
第四行包含n个整数,其中第i个整数表示第i堆石子的数量hi。
输出格式
如果先手方必胜,则输出“Yes”。
否则,输出“No”。

数据范围

1≤n,k≤1001≤n,k≤100,
1≤si,hi≤10000

输入样例:

2
2 5
3
2 4 7

输出案例

Yes

Nim游戏
首先需要满足固定条件
1,由两名玩家交替行动
2,在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关
3,不行行动的玩家判负
此时称该游戏为一个公平组合游戏
Nim博弈属于公平组合游戏,但城建的棋类游戏,比如围棋就不是公平组合游戏。

⊕为异或符号
首先分三种请况
1:所有堆中均无棋子即所有堆中全为0
此时 0⊕0⊕0……⊕0=0

2:在初始状态各个堆中均有不同的数,且此时在该状态下有先手必胜
此时
a1⊕a2⊕a3⊕……⊕an=x≠0

x的二进制表示1中最高的一位1在第a1~an中必然存在一个ai
ai的第k为是1
假设当
ai的二进制为 11001001101
x的二进制为 1010010
ai⊕x 为 11000,

由此可知ai⊕x<ai
由于 ai-(ai-ai⊕x)=ai⊕x
因此此处可以用ai^x替换掉ai
a1⊕a2⊕a3⊕…ai⊕x⊕a(i+1)⊕ …⊕an
又因为a1⊕a2⊕a3⊕……⊕an=x
因此
a1⊕a2⊕a3⊕…ai⊕x⊕a(i+1)⊕ …⊕an=x⊕x=0

3:在初始状态各个堆中均有不同的数,且此时在该状态下有先手必败
此时
a1⊕a2⊕a3⊕……⊕an=0
又有
a1⊕a2⊕a3⊕…ai’⊕a(i+1)⊕…⊕an=0
此时两式不相同的仅ai与ai’
因此可得
ai⊕ai’=0
因此
ai=ai’
即如若拿取任意一个堆中的物品都会出现存在先手必胜的情况

由此可推,当初始状态不满住全为0时
如若初始存在先手必胜,当先手的玩家选取后,后手的玩家则存在先手必败的情况
如若初始存在先手必败,当先手的玩家选取后,后手的玩家则存在先手必胜的情况
因此我们仅需求出初始状态下的存在的先手必败或者先手必胜的状态即可。

在题目中,有n堆石子,同时有一个集合S,玩家操作取出某个堆中的石子的数量必须与集合S中的某一个元素的量相同。因此对于一个请况存在以集合S中的元素数为划分的变化情况。

由此我们可以使用SG函数的方式来计算

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)。

Mex运算

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

按照SG函数的定义
我们模拟如下图

初始状态如下图我们设定点6与点7的值为0
在这里插入图片描述

由SG函数的结果可知,点4可到0,那么到0前的最小值为1故点4的值为1,同理点5也为1 如下图

在这里插入图片描述
然后点3可到1与0,那么根据SG函数,点3取除能到的数以外的最小值,则点3为2 如下图

在这里插入图片描述
然后点2可到1,那么根据SG函数,点2取除能到的数以外的最小值,则点2为0 如下图
在这里插入图片描述
然后点1可到0,1,2,那么根据SG函数,点1取除能到的数以外的最小值,则点1为3 如下图

在这里插入图片描述
这是在处理该类型题目时的某一种情况后的SG函数的结果。

对于该题可能存在多种上述的不同的有向图,即多个SG函数的值。

由于在该题中我们每一堆石子均可操作,且有集合S内元素的情况。因此我们可以得到每一堆地SG值而后对每一堆地SG值进行异或计算。结果为0则先手必败,否则为先手必胜。

使用异或的结果来判断的原因参考经典Nim游戏的结论·。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;
const int N=110,M=10010;
int s[N],f[M],k,n;
int sg(int t)
{
    if(f[t]!=-1)return f[t];
    unordered_set<int> map;
    for(int i=0;i<n;i++)
    {
        int num=s[i];
        if(t>=num)map.insert(sg(t-num));
    }
    
    for(int i=0;;i++)if(!map.count(i))return f[t]=i;
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)cin>>s[i];
    memset(f,-1,sizeof(f));
    cin>>k;
    int ans=0;
    while(k--)
    {
        int t;
        cin>>t;
        ans^=sg(t);
    }
    ans?cout<<"Yes":cout<<"No";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值