【Leetcode-每日一题】石子游戏 IX

石子游戏 IX
难度:简单
在这里插入图片描述
在这里插入图片描述
每天的痛苦从博弈题开始/(ㄒoㄒ)/~~
为了方便,我们用 A 来代指 Alice,用 B 带代指 Bob。

A 只有一种获胜方式,是使得 B 在选石子时凑成 3 的倍数;而 B 除了能够通过让 A 凑成 3 的倍数以外,还能通过让游戏常规结束来获胜。

因此整个游戏过程,我们只需要关心「已被移除的石子总和」和「剩余石子个数/价值情况」即可。

更进一步的,我们只需关心已被移除的石子总和是否为 3 的倍数,以及剩余石子的价值与已移除石子总和相加是否凑成 3 的倍数即可。

所以我们可以按照石子价值除以 3 的余数分成三类,并统计相应数量。

不失一般性考虑,某个回合开始前,已移除的石子总和状态为 x(共三种,分别为除以 3 余数为 0、1 和 2,其中当状态为 0,且非首个回合时,说明凑成 3 的倍数,游戏结束),剩余石子价值除以 3 的余数 s 分别为 0、1 和 2。
首先如果当前 x = 1 时,不能选择 s = 2 的石子,否则会导致凑成总和为 3 的倍数而失败;同理 x = 2 时,不能选择 s = 1 的石子;而选择 s = 0 的数字,不会改变 x 的状态,可看做换手操作。

同时成对的 s = 0 的等价于没有 s = 0 的石子(双方只需要轮流选完这些 s = 0 的石子,最终会回到先手最开始的局面);而选择与 x 相同的 s 会导致 x 改变(即 x = 1 时,选择 s=1 的石子,会导致 x = 2;而 x=2 时,选 s = 2 的石子,会导致 x = 1)。

明确规则后,是分情况讨论的过程:

  • s = 0 的石子数量为偶数:此时等价于没有 s = 0 的石子,我们只需要关心 s = 1 和 s = 2 即可:
    • s = 1 的石子数量为 0: 这意味着 A 开始选择的只能是 s = 2,此时交给 B 的局面为「x = 2、剩余石子只有 s = 2」,此时 B 只能选 s = 2 的石子,由于 x = 2 且选择的石子 s = 2,因此交由回 A 的局面为「x = 1,剩余是在只有 s = 2」,因此游戏继续的话 A 必败,同时如果在该过程的任何时刻石子被取完,也是 B 直接获胜,即 A 仍为必败;
    • s = 2 的石子数量为 0:分析同理,A 只能选 s = 1,此时交给 B 的局面为「x = 1、剩余石子只有 s = 1」,此时 B只能选 s = 1 的石子,由于 x = 1 且选择的石子 s = 1,因此交由回 A 的局面为「x = 2,剩余是在只有 s = 1」,因此游戏继续的话 A 必败,同时如果在该过程的任何时刻石子被取完,也是 B 直接获胜,即 A 仍为必败;
    • s = 1 和 s = 2 的石子数量均不为 0:A 选数量不是最多的一类石子,B 下一回合只能选择相同类型的石子(或是无从选择导致失败),然后游戏继续,最终 B 会先进入「只能凑成 3 的倍数」的局面导致失败,即 A 必胜。

s = 0 的石子数量为奇数:此时等价于有一次换手机会,该换手机会必然应用在「对必败局面进行转移」才有意义,因此只有 s = 1 和 s = 2 的石子数量差大于 2,A 的先手优势不会因为存在换手机会而被转移:

  • 两者数量差不超过 2:此时 B 可以利用「对方凑成 3 的倍数必败」规则和「优先使用 s = 0 石子」权利来进入确保自己为必胜态:

    • 举个例子,当 s = 1 和 s = 2 的石子数量相等,虽然有 s = 0 的石子,A 先手,但是 A 的首个回合必然不能选 s = 0,否则马上失败结束,因此 A 只能选 s = 1 或 s = 2,此时 B直接选择 s = 0 的石子,交由给 A 的局面 x 没有发生改变,A 只能选择与首个回合相同的 s 游戏才能继续,因此局面会变为「B 先手、s = 1 和 s = 2 的石子数量差为 2」,游戏继续,最终 A 会先遇到「只能凑成 3 的倍数」的局面,即 B 必胜。

    • 两者数量差不超过 2:此时无论 A 选择数量较少或较多的 s,B 都在第二回合马上使用 s = 0 的石子进行换手,A
      只能继续选与第一回合相同类型的的石子,游戏才能进行,最终 A 会先遇到「只能凑成 3 的倍数」或「石子被取完」的局面,即 B
      必胜

  • 两者数量差超过 2 :此时即使 A 只要确保第一次选择数量较多的 s,不管 B 是否使用「优先使用 s = 0」的石子,A 都有足够次数数量多 s 来抵消换手(或是在 B 放弃使用 s = 0 之后马上使用),最终都是 B 最先遇到「只能凑成 3 的倍数」的局面,即 A 获胜

代码如下:

	public boolean stoneGameIX(int[] stones) {
        int[] cnt = new int[3];
        for (int stone : stones){
            cnt[stone%3]++;
        }
        if (cnt[0]%2==0){
            if (cnt[1] == 0 || cnt[2] == 0){
                return false;
            }else{
                return true;
            }
        }else{
            if (cnt[1] - cnt[2] > 2 || cnt[1] - cnt[2] < -2){
                return false;
            }else{
                return true;
            }
        }
    }

执行结果:成功
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值