博弈论(Nim 游戏进阶)

这篇博客探讨了一种Nim游戏的变种,其中玩家在楼梯台阶上轮流取石子。关键在于分析奇数台阶的石子异或和,当其为0时,先手必败;否则,先手必胜。博主通过证明展示了如何通过策略确保胜利,即使对手从偶数台阶取石子,也能保持奇数台阶异或和不变,从而维持必胜状态。
摘要由CSDN通过智能技术生成

892. 台阶-Nim游戏

现在,有一个 n

级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i≥1

)。

两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。

已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。

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

输入格式

第一行包含整数 n

第二行包含 n

个整数,其中第 i 个整数表示第 i 级台阶上的石子数 ai

输出格式

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

否则,输出 No

数据范围

1≤n≤105

,
1≤ai≤109

输入样例:

3
2 1 3

输出样例:

Yes

 

Nim游戏的变种;
 * 刚开始的时候没想出来,最开始的时候,知道应该要分为奇数阶和偶数阶来讨论,于是有了
 * 下面的想法:
 * 当偶数阶的异或和为0的时候,先手胜,反之先手败;(现在想想这种猜测是错误的)
 * 当奇数阶的异或和为0的时候,先手败,反之先手胜;
 *
 * 于是两种情况不知道怎么判断,emm,然后看了别人的题解,看了一眼结论:就是
 * 当奇数阶的异或和为0的时候,先手败,反之先手胜;这个结论。
 * 随后接着来思考,想出了其中的道道。
 *
 * 给出当奇数阶的异或和为0的时候,先手败,反之先手胜的证明;
 *  证明:
 *  通过Nim游戏我们已经证明了,当奇数阶的异或和为0的时候,取一个正整数之后,
 *  异或和一定不为0,那么对手一定能取一个数使之异或和变为0;这样进行n轮循环
 *  之后,一定使先手没有石子可拿,所以先手败北。反之,当奇数阶的异或和不为0的时候,
 *  必然是先手胜。
 *  然后,我们来唠唠对手取双数阶上的石子的时候,怎么办。当奇数阶的异或和不为0的时候,
 *  我使取一个数使之异或和变为0,轮到对手,对手取偶数阶上的石子时,对手取多少到石子
 *  到下一阶的台阶上,我们就把他放到单数阶上的石子数原数放到下一阶去,这就使得奇数
 *  阶的异或和还是为0(因为每一个单数阶上的石子都没变)。
 *

 *  核心:当异或和不为0,先手总是取一个数使必败状态留给对方。
 *  当异或和为0(先手处于必败状态),先手不管怎么操作,后手都会把必败状态还给先手
 *  (别想给我下套);

/**
 * Nim游戏的变种;
 * 刚开始的时候没想出来,最开始的时候,知道应该要分为奇数阶和偶数阶来讨论,于是有了
 * 下面的想法:
 * 当偶数阶的异或和为0的时候,先手胜,反之先手败;(现在想想这种猜测是错误的)
 * 当奇数阶的异或和为0的时候,先手败,反之先手胜;
 * 
 * 于是两种情况不知道怎么判断,emm,然后看了别人的题解,看了一眼结论:就是
 * 当奇数阶的异或和为0的时候,先手败,反之先手胜;这个结论。
 * 随后接着来思考,想出了其中的道道。
 * 
 * 给出当奇数阶的异或和为0的时候,先手败,反之先手胜的证明;
 *  证明:
 *  通过Nim游戏我们已经证明了,当奇数阶的异或和为0的时候,取一个正整数之后,
 *  异或和一定不为0,那么对手一定能取一个数使之异或和变为0;这样进行n轮循环
 *  之后,一定使先手没有石子可拿,所以先手败北。反之,当奇数阶的异或和不为0的时候,
 *  必然是先手胜。
 *  然后,我们来唠唠对手取双数阶上的石子的时候,怎么办。当奇数阶的异或和不为0的时候,
 *  我使取一个数使之异或和变为0,轮到对手,对手取偶数阶上的石子时,对手取多少到石子
 *  到下一阶的台阶上,我们就把他放到单数阶上的石子数原数放到下一阶去,这就使得奇数
 *  阶的异或和还是为0(因为每一个单数阶上的石子都没变)。
 * 
 *  核心:当异或和不为0,先手总是取一个数使必败状态留给对方。
 *  当异或和为0(先手处于必败状态),先手不管怎么操作,后手都会把必败状态还给先手
 *  (别想给我下套);
*/

#include <iostream>

using namespace std;

int main()
{
    int n;
    cin >> n;
    
    int res=0;
    for(int i=1;i<=n;++i)
    {
        int val;
        cin >> val;
        
        if(i%2)
            res^=val;
    }
    
    if(res)
        puts("Yes");
    else
        puts("No");
        
    return 0;
}

894. 拆分-Nim游戏

给定 n

堆石子,两位玩家轮流操作,每次操作可以取走其中的一堆石子,然后放入两堆规模更小的石子(新堆规模可以为 0

,且两个新堆的石子总数可以大于取走的那堆石子数),最后无法进行操作的人视为失败。

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

输入格式

第一行包含整数 n

第二行包含 n

个整数,其中第 i 个整数表示第 i 堆石子的数量 ai

输出格式

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

否则,输出 No

数据范围

1≤n,ai≤100

输入样例:

2
2 3

输出样例:

Yes

想想Mex函数的定义,想想SG函数的定义:

#include <iostream>
#include <cstring>
#include <set>

using namespace std;

const int maxn = 110;
int h[maxn];

int SG(int x)
{
    if(h[x] != -1)
        return h[x];
    
    set<int> st;
    for(int i=0;i<x;++i)
        for(int j=0;j<=i;++j)
            st.insert(SG(i)^SG(j));
            
    for(int i=0;;++i)
        if(!st.count(i))
            return h[x]=i;
}

int main()
{
    memset(h, -1, sizeof h);
    
    int n;
    cin >> n;
    
    int res=0;
    
    while (n -- )
    {
        int x;
        cin >> x;
        res^= SG(x);
    }
    
    if(res)
        puts("Yes");
    else    
        puts("No");
    
    return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值