java---博弈论---基础Nim游戏(每日一道算法2022.10.1)

注意事项:
尽量先看懂思路,代码其实真的很简单,没什么可说的

题目:
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败

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

第一行包含整数 n
第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量

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

输入:
2
2 3
输出:
Yes
public class 博弈论_Nim游戏 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        
        //将理论用在代码上很简单,就是将所有堆的石子数量异或一下,如果不为0就是先手必胜,为0就是先手必败
        int res = 0;
        while (n-- > 0) {
            int x = in.nextInt();
            res ^= x;
        }

        if (res != 0) System.out.println("Yes");
        else System.out.println("No");
    }
}

思路:
首先结论,Nim游戏存在先手必胜法
例如:有两堆石子,第一堆有2个,第二堆有3个,先手必胜
步骤:
1.先手从第二堆拿走1个,此时第一堆和第二堆数目相同
2.无论后手怎么拿,先手都在另外一堆石子中取走相同数量的石子即可

然后我们来了解两个名词:先手必胜状态和先手必败状态
先手必胜状态:先手操作完,可以让后手走到某一个必败状态:a1 ^ a2 ^ a3 ^ … ^ an = x ≠ 0
先手必败状态:先手操作完,后手走不到任何一个必败状态:a1 ^ a2 ^ a3 ^ … ^ an = x = 0
(^符号表示异或,是二进制位运算符,相同为0,不同为1)

三条证明:
1.根据游戏规则,我们可以得知当每一堆的石子数量都为0时:0 ^ 0 ^ 0 ^ … ^ 0 = 0,游戏结束

2.在操作过程中,如果 a1 ^ a2 ^ a3 ^ … ^ an = x ≠ 0那么玩家必然可以通过拿走某一堆若干个石子将异或结果变为0
证明:假设x的二进制中最高位的1在第k位,那么在a1,a2…an中,必然有一个数ai的第k位为1,且ai ^ x < ai, 那么从ai中拿走(ai - (ai ^ x))个石子,ai中还剩ai - (ai - (ai ^ x))个石子,而ai - (ai - (ai ^ x)) = ai ^ x, 那么当前公式就变为a1 ^ a2 ^ a3 ^ … ^ ai ^ x ^ … ^ an = x ^ x = 0

3.在操作过程中,如果a1 ^ a2 ^ a3 ^ … ^ an = x = 0,那么无论玩家怎么拿,必然会导致最终异或结果不为0
证明:这里就反证一下就可以,如果从ai堆中拿了石子之后记作ay,其他堆不变,异或结果还是为0,那么ai就一定等于ay,这就矛盾了

基于以上三条证明,可以得知:
1.如果先手面对的局面是a1 ^ a2 ^ a3 ^ … ^ an = x ≠ 0,那么先手总可以通过拿走某一堆若干个石子,将局面变成a1 ^ a2 ^ a3 ^ … ^ an = x = 0。如此重复,最后一定是后手面临最终没有石子可拿的状态。
先手必胜。

2.如果先手面对的局面是a1 ^ a2 ^ a3 ^ … ^ an = x = 0,那么无论先手怎么拿
都会将局面变成a1 ^ a2 ^ a3 ^ … ^ an = x ≠ 0,那么后手总可以通过拿走某一堆若干个石子,将局面变成a1 ^ a2 ^ a3 ^ … ^ an = x = 0。如此重复,最后一定是先手面临最终没有石子可拿的状态。
先手必败。

声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值