LeetCode——2029.石子游戏

大佬,牛!!!

  • 题目:是一个博弈类型的题目,这个题目主要是思路比较难想。给你一堆石子,然后每次A、B两人每次从里面取一个。然后判断输赢。游戏规则:
    • 如果这个人取出来以后,所有的取出之和是3的倍数,则这个人输了。
    • 如果这个人取出来以后,没有棋子了,并且不是3的倍数,则一定是B赢。
    • 如果A赢就返回true,B赢返回false。
  • 思路:首先明确几点,肯定不是遍历所有情况,也无法遍历,因为每个人都是最聪明的,我们很难取找条件。所以题目一定是找规律。
    • 首先,我们可以发现,每次取我们其实不用管这个数是多少,我们只需要关心这个数对3取余。因为这个才会影响胜负,这一点一定明确。然后就是找规律了。对3取余以后的情况有三种:0、1、2。因此下面我们值考虑余数。
    • 我们先看0的时候,0其实改变了谁能多取一次,如果0的个数是偶数,则每个人取0的次数一样,如果是奇数,则B可以多选择一次这个0,并且不会输掉比赛。
    • 然后就是关键了,我们让A赢,则一定是B输,而B只有一种情况会输,就是拿出来是3的倍数。
    • 然后问题就简单了,我们想办法让B只能是拿出来是3的倍数就好了。
    • 首先我们假设0的个数是偶数,则其实0在这里就没有什么意义了。
    • 然后A先拿1,
      • 则B一定是1,不然就输了,因此如果有只有一个1的时候,只要有一个2,A就赢了(也就是只有一个1,并且有2的时候,返回true)
      • B能拿到1,当前情况为取了1、1,接下来要想一直下去A只能拿2、B只能拿1,序列为:112121…,这时候要想B输,则让B取不到1即可,这时候的条件是2的个数大于等于1的个数,并且前提是1的个数大于两个(因为,本条的前提是B能拿到一个1,并且A先拿的1,这里一共用了两个1)(也就是说1的个数大于两个,并且2的个数大于等于1的个数,返回true)。
    • 然后看A先拿2,
      • 则B一定是2,不然B就输了(也就是只有一个2,并且有1的时候,返回true)。
      • 则B能拿到2,当前情况为取了2、2,接下来要想一直下去A只能拿1、B只能拿2,序列为221212…,这时候要想B输,则让B取不到2即可,这时候的条件是1的个数大于等于2的个数,并且前提是2的个数大于两个(因为,本条的前提是B能拿到一个2,并且A先拿的2,这里一共用了两个2)(也就是说2的个数大于两个,并且1的个数大于等于2的个数,返回true)。
    • 然后看0是奇数的时候,也就是多一个0。
    • A先拿1,
      • 则B如果没有1,B还可以取一个0,然后由于A只能取1了,因此B是可以取1的。
        • 序列为101212…,这时候要想B输,则B取不到2即可(只能取1),即1的个数大于等于2的个数+三
        • 序列为112121…,这时候要想B输,则B取不到1即可,但是还能取0,则B取0,序列为112120,然后A只能取1,因此B能取到1。也就是这个0一定是由A来取,那直接构建一个序列110212…,但是要B输,取不到最后的2,也就是1的个数大于等于2的个数+三。
      • 结论:1的个数大于等于2的个数+三,则A一定获胜
    • A先拿2,
      • 结论相反:2的个数大于等于1的个数+三
  • 技巧:
    • 没有特别多的技巧,主要是思路。
    • 使用余数进行,这个将余数个数放在一个数组中

伪代码

定义余数数组
遍历填写数组内容
如果0 的个数是偶数
    四种情况会返回true
    否则返回false
否则,0的个数是偶数
    两种情况会返回true。

java代码

class Solution {
    public boolean stoneGameIX(int[] stones) {
        // 0会影响谁先手
        int nums[] = new int[3];// 0,1,2的余数的个数
        for (int i = 0; i < stones.length; i++) {
            nums[stones[i] % 3]++;
        }
        if (nums[0] % 2 == 0) {// 0的个数是偶数
            if (nums[1] == 1 && nums[2] >= 1) return true;// 余数是1的个数是1个,这时候要想A获胜,则余数是2的个数必须大于等于1
            if (nums[1] >= 2 && nums[2] >= nums[1]) return true;// 余数大于等于2,这时候A想获胜,则余数是2的必须
            if (nums[2] == 1 && nums[1] >= 1) return true;
            if (nums[2] >= 2 && nums[1] >= nums[2]) return true;
            return false;
        } else {// 0的个数是奇数
            return nums[2] - nums[1] >= 3 || nums[1] - nums[2] >= 3;
        }
    }
}
  • 总结:这种博弈的题目一定是找规律,然后我们就让一个赢,站在一个人的角度上去想,找到所有情况以后,其余情况返回相反的结果即可。这里原文链接其实并不详细,主要是给了两个有规律的思路,在这里附一下链接

代码优化,四种情况可以优化为如下代码

class Solution {
    public boolean stoneGameIX(int[] stones) {
        // 0会影响谁先手
        int nums[] = new int[3];// 0,1,2的余数的个数
        for (int i = 0; i < stones.length; i++) {
            nums[stones[i] % 3]++;
        }
        if (nums[0] % 2 == 0) {// A先手
            return nums[1] >= 1 && nums[2] >= 1;
        } else {// 其实是b先手
            return nums[2] - nums[1] >= 3 || nums[1] - nums[2] >= 3;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值