博弈论


前言

复习acwing算法基础课的内容,本篇为讲解数学知识:博弈论,关于时间复杂度:目前博主不太会计算,先鸽了,日后一定补上。


一、博弈论

一个游戏如何被称为是一个公平的游戏:
1.由两名玩家一人一轮操作
2.在游戏进行的任意时刻,可以执行的合法行动与轮到哪位玩家无关
3.不能行动的玩家输掉了比赛

有关博弈论这里介绍两个状态

必胜状态:先手进行某一个操作,留给后手是一个必败状态时,对于先手来说是一个必胜状态。即先手可以走到某一个必败状态

必败状态:先手无论如何操作,留给后手都是一个必胜状态时,对于先手来说是一个必败状态。即先手走不到任何一个必败状态


二、例题,代码

AcWing 891. Nim游戏

本题链接:AcWing 891. Nim游戏
本博客提供本题截图:
在这里插入图片描述

本题分析

假设有n堆石子,石子的个数分别为:a1,a2,a3,...an 如果a1^a2^a3^...^an != 0则为先手必胜,反之则为先手必败.

AC代码

#include <iostream>

using namespace std;

int main()
{
    int n;
    cin >> n;
    
    int res = 0;
    while (n -- )
    {
        int a;
        cin >> a;
        res ^= a;
    }
    
    if (res) puts("Yes");
    else puts("No");
    
    return 0;
}

AcWing 892. 台阶-Nim游戏

本题链接:AcWing 892. 台阶-Nim游戏
本博客提供本题截图:
在这里插入图片描述

本题分析

如果先手时奇数台阶上的值的异或值为0,则先手必败,反之必胜

AC代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int main()
{
    int n;
    scanf("%d", &n);

    int res = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int x;
        scanf("%d", &x);
        if (i & 1) res ^= x;
    }

    if (res) puts("Yes");
    else puts("No");

    return 0;
}

AcWing 893. 集合-Nim游戏

本题链接:AcWing 893. 集合-Nim游戏
本博客提供本题截图:
在这里插入图片描述

本题分析

1.Mex运算:
设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即:
mes(S)=min{x};
例如:S={0,1,2,4},那么mes(S)=3;

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

AC代码

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

using namespace std;

const int N = 110, M = 10010;

int n, m;
int s[N], f[M];

int sg(int x)
{
    if (f[x] != -1) return f[x];
	//记忆化搜索,因为取石子数目的集合是已经确定了的,所以每个数的sg值也都是确定的,如果存储过了,直接返回即可
    unordered_set<int> S;
    //注意,因为是在函数内部定义的set,所以每次递归中的S是不一样的
    for (int i = 0; i < m; i ++ )
    {
        int sum = s[i];
        if (x >= sum) S.insert(sg(x - sum));
        //先遍历至终点,然后倒推求sg
    }

    for (int i = 0; ; i ++ )
        if (!S.count(i))
            return f[x] = i;
}

int main()
{
    cin >> m;
    for (int i = 0; i < m; i ++ ) cin >> s[i];
    cin >> n;

    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;
}

AcWing 894. 拆分-Nim游戏

本题链接:AcWing 894. 拆分-Nim游戏
本博客提供本题截图:
在这里插入图片描述

本题分析

本题相比于上一题,为一个情况拆分成了多个的小情况,根据sg函数,我们需要存储的状态为sg(i) ^ sg(j)

AC代码

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

using namespace std;

const int N = 110;

int n;
int f[N];

int sg(int x)
{
    if (f[x] != -1) return f[x];
	//记忆化搜索
    unordered_set<int> S;
    for (int i = 0; i < x; i ++ )
        for (int j = 0; j <= i; j ++ )//规定j不大于i,避免重复
            S.insert(sg(i) ^ sg(j));

    for (int i = 0;; i ++ )
        if (!S.count(i))
            return f[x] = i;
}

int main()
{
    cin >> n;

    memset(f, -1, sizeof f);

    int res = 0;
    while (n -- )
    {
        int x;
        cin >> x;
        res ^= sg(x);
    }

    if (res) puts("Yes");
    else puts("No");

    return 0;
}

三、时间复杂度

关于博弈论各步操作的时间复杂度以及证明,后续会给出详细的说明以及证明过程,目前先鸽了。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值