博弈算法

数学知识

首先补充一点数学知识

SG函数

mex运算

m e x ( a 1 , a 2... a n ) 的 结 果 为 这 个 集 合 内 没 有 出 现 过 的 最 小 正 数 mex(a1,a2...an)的结果为这个集合内没有出现过的最小正数 mex(a1,a2...an)

SG函数的取值

若 S G ( x ) 可 到 达 的 情 况 为 S G ( a 1 , a 2 . . . . a n ) 那 么 S G ( x ) = m e x ( a 1 , a 2 . . . . a n ) 若SG(x)可到达的情况为SG(a_{1},a_{2}....a_{n})\\ 那么SG(x)=mex(a_{1},a_{2}....a_{n}) SG(x)SG(a1,a2....an)SG(x)=mex(a1,a2....an)

一 巴什博弈

1 定义

 假设这里有一堆数量为n的石子,有两个人从石堆里吗取出1~m颗石子,最终没有石子可取的那个人输。

2 输赢情况

情况一:
n = m + 1 先 手 取 s ( 1 ~ m ) 个 , 后 者 再 取 n − s 个 , 先 者 必 输 \quad n=m+1 \\ 先手取s(1~m)个,后者再取n-s个,先者必输\\ n=m+1s(1m)ns
情况二:
n = ( m + 1 ) ∗ r 先 手 每 次 取 s ( 1   m ) 个 , 后 手 每 次 取 n − s 个 , 最 终 还 是 后 手 必 赢 \quad n=(m+1)*r\\ 先手每次取s(1~m)个,后手每次取n-s个,最终还是后手必赢\\ n=(m+1)rs(1 m)ns
情况三:
n = ( m + 1 ) ∗ r + x 先 手 取 x 个 , 那 么 后 手 遍 置 于 情 况 二 下 , 先 手 必 赢 \quad n=(m+1)*r+x 先手取x个,那么后手遍置于情况二下,先手必赢 n=(m+1)r+xx

3 代码实现

#include <stdio.h>
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    if(n%(m+1)) printf("first win");
    else printf("lose");
    return 0;
}

二 巴什博弈的变形

1 定义

 假设这里有一堆数量为n的石子,每次只能从其中拿走限定数量的石子,求必胜局面

2 输赢情况

 若能取的石子数量为1 3 4,当石子数量为0时,先手必输,当数量为1 3 4 时,后手必输,以此递推,每个局面都是非赢即输,且可以在一步之内转化,这种结果很类似于SG函数若该状态能取到必输状态,即为必赢状态。

3 代码实现

#include <stdio.h>
#include <string>
int choices[3]={1,3,4};
int vis[100];
int SG[100];
int mex(int n)
{
    for (int i=0; i<=n; i++)
    {
        if(!vis[i])
            return i;
    }
    return -1;
}//查找mex的值
void GetSG(int n)
{
    for (int i=1; i<=n; i++)
    {
        memset(vis, 0, sizeof(vis));//初始化vis为0
        for (int j=1; j<=3&&choices[j]<i; j++)
        {
            vis[SG[i-choices[j]]]=1;//将可取到的vis设为1
        }
        SG[i]=mex(i);
    }
}

三 威佐夫博弈 ( Wythoff’s Game )

1 定义

 有两堆数量分别为a,b的石子,有两个人依次从其中取出石子,有两种取石子的方法,从一堆石子中取出任意个和从两堆石子中取出同样多个。

2 输赢情况

 在威佐夫博弈中存在先手必输的局面,我们将这种局面称为奇异局势,用(a , b)表示现在两堆的状况
 已知(0, 0)为奇异局势,那么(k, 0), (0, k), (k, k)都为非奇异局势,据此类推我们找到后面的几种奇异局势(1, 2), (3, 5), (4, 7), (6, 10)…
 据此我们进行假设
1. a p 为 之 前 未 出 现 过 的 最 小 正 整 数 2. b p = a p + p \begin{aligned} &1.\quad a_{p}为之前未出现过的最小正整数\\ &2.\quad b_{p}=a_{p}+p\\ \end{aligned} 1.ap2.bp=ap+p
进行数学归纳法证明
假 设 在 之 前 的 k ∈ [ 1 , n ] , ( a k , a k + n + 1 ) 都 为 奇 异 局 势 , 我 们 只 需 证 明 ( a n + 1 , a n + 1 + n + 1 ) 为 奇 异 局 势 1. 从 左 边 拿 一 点 , 因 为 比 a n + 1 小 的 数 都 在 前 面 存 在 过 , 所 以 只 需 要 在 右 边 取 适 当 的 数 就 可 变 化 为 奇 异 局 势 2. 从 右 边 拿 一 点 , 如 果 右 边 取 的 太 多 , 那 么 转 化 为 情 况 一 , 如 果 拿 的 较 少 , 则 在 左 边 取 相 应 的 数 转 换 为 前 面 的 奇 异 局 势 3. 从 两 边 同 时 拿 一 点 , 则 可 以 直 接 取 为 前 面 的 一 个 奇 异 局 势 。 \begin{aligned} &假设在之前的k\in[1,n],(a_{k},a_{k}+n+1)都为奇异局势,\\ &我们只需证明(a_{n+1},a_{n+1}+n+1)为奇异局势\\ &1.从左边拿一点,因为比a_{n+1}小的数都在前面存在过,所以只需要在右边取适当的数就可变化为奇异局势\\ &2.从右边拿一点,如果右边取的太多,那么转化为情况一,如果拿的较少,则在左边取相应的数转换为前面的奇异局势\\ &3.从两边同时拿一点,则可以直接取为前面的一个奇异局势。 \end{aligned} k[1,n],(ak,ak+n+1)(an+1,an+1+n+1)1.an+12.3.
 并且对于每一个非奇异局势,都可在一次取数转化为一个奇异局势面对非奇异局势,先拿者必胜。
 我们可以发现对于 a n a_{n} an b n b_{n} bn它们很像Beatty数列。
{ 1 α + 1 β = 1 ⌊ a n ⌋ = α n ⌊ b n ⌋ = β n 由 a n + n = ( α + 1 ) n = β n , 得 β = α + 1 带 入 上 式 解 出 α = 5 + 1 2 , 由 此 我 们 可 以 得 到 a n 和 b n 的 通 项 , 满 足 此 通 项 的 坐 标 \left\{ \begin{array}{ll} \frac{1}{\alpha} +\frac{1}{\beta}=1\\ \lfloor a_{n} \rfloor=\alpha n \quad\lfloor b_{n} \rfloor=\beta n\\ \end{array} \right. \\ 由a_{n}+n=(\alpha +1)n=\beta n,得\beta=\alpha +1\\带入上式解出 \alpha=\frac{\sqrt{5}+1}{2},由此我们可以得到a_{n}和b_{n}的通项,满足此通项的坐标 {α1+β1=1an=αnbn=βnan+n=(α+1)n=βn,β=α+1α=25 +1anbn

3 代码实现

#include <stdio.h>
#include <math.h>
double use = (sqrt(5.0)+1)/2;
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    if(n<m)
    {
        int temp=n;
        n = m;
        m = temp;
    }
    int a = n - m;
 //   int ans = use * a;
    if(m == (int)(use * a))//如果刚好为n乘以use,则为奇异局势,先手必输
        printf("0");
    else
        printf("1");
}

四 Nim游戏

1 定义

 有n堆石子,每人每次可以从一堆石子中取出任意颗石子,无法操作的一方输

2 输赢情况

 当所有堆的石子都为0的时候,那么每一堆数目的异或和都为0,在后面的任意一种状态下,每一堆的异或和都可以通过一步变化变为0,具体操作为将使得最高位为1的数单独提出,使他变为剩下数的异或和,从而达到后手必输的局面

3 代码实现

#include <stdio.h>
#include <math.h>
int main()
{
    //for (int i=1; i<=10001; i++) f[i]=i;
    int T;
    int a[100010];
    scanf("%d",&T);
    while (T--)
    {
        int n;
        scanf("%d",&n);
        int ans=0;
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            ans^=a[i];
        }
        if (ans) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

先更新到这里,去写作业了

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值