博弈论-集合-Nim游戏

相关概念:

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

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)。SG(终点)=0,存在y1, y2, ... ,yn的局面:

这里面不存在的最小自然数。如下图所示,图的最后一个点(第二条路径)即终点定义为0,终点前一个点不存在最小自然数则为1,往后一个数不存在最小自然数除了0和1,那么就是2了,如此递推,最开始那个不存在最小自然数为3。

结论:SG = 0,必败,SG != 0必败。

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

Example

给定nn堆石子以及一个由kk个不同正整数构成的数字集合SS。

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

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

输入格式

第一行包含整数kk,表示数字集合SS中数字的个数。

第二行包含kk个整数,其中第ii个整数表示数字集合SS中的第ii个数sisi。

第三行包含整数nn。

第四行包含nn个整数,其中第ii个整数表示第ii堆石子的数量hihi。

输出格式

如果先手方必胜,则输出“Yes”。

否则,输出“No”。

数据范围

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

输入样例:

2
2 5
3
2 4 7

输出样例:

Yes

分析:根据例子画出有向图得到初始状态SG的值

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>

using namespace std;

const int N = 110, M = 10010;

int n, m;
int s[N], f[M];//s表示谁的个数,f表示SG的值

//求sg
int sg(int x)
{
    //记忆化搜索--优化
    if (f[x] != -1) return f[x];//如果每个状态都被算过直接返回结果
    
    //哈希表存他所有可以到的局面
    unordered_set<int> S;
    for (int i = 0; i < m; i ++)
    {
        //当前数的个数
        int sum = s[i];
        //如果当前个数>=sum,才可以取这些数
        if (x >= sum) S.insert(sg(x - sum));
    }
    //判断当前集合当中不存在的点
    for (int i = 0; ; i ++)
        if (!S.count(i))//如果当前数不存在,当前的值就是i
            return f[x] = i;
}

int main()
{
    cin >> m;//读入m个数
    //输入操作
    for (int i = 0; i < m; i ++) cin >> s[i];
    cin >> n;//读入n,求的SG,每堆的个数
    
    memset(f, -1, sizeof f);//用记忆化搜索
    
    //求每一堆的个数
    int res = 0;
    for (int i = 0; i < n; i ++)
    {
        int x;
        cin >> x;
        res ^= sg(x);
    }
    
    if (res) puts("Yes");
    else puts("No");
    
    return 0;
}
发布了176 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_27262727/article/details/104669959