首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >292. Nim 游戏

292. Nim 游戏

作者头像
飞询
发布2025-08-01 16:48:32
发布2025-08-01 16:48:32
7400
代码可运行
举报
文章被收录于专栏:云同步云同步
运行总次数:0
代码可运行

292. Nim 游戏 - 力扣(LeetCode)

想法

枚举问题:

  • n = 1 ~ 3 ,由于我先手,我可以直接拿走全部的石头,所以我赢
  • n = 4,由于我先手,我拿掉 1 - 3 块石头 ,剩下的可能就是 1 - 3 块石头,他可以拿掉全部石头,所以他赢
  • n = 5,假设都是在最优的情况下,我拿掉 1 块石头,他最多只能拿 1 - 3 块,不管他拿多少,剩下的我都能拿到

这个游戏的问题就类似于斐波那契数列,我的最优答案可以从前三个里面获取,由于我先手,只要他不赢那就是我赢

代码语言:javascript
代码运行次数:0
运行
复制
// (n - 1) && (n -2) && (n -3):这些是 n - 1,n - 2,n - 3 只剩这些石头,出手会不会赢
// &&:我当然不会让他赢啦,只要有一个能让他输,我就拿多少石头
// !:((n - 1) && (n -2) && (n -3)) 用来判断他是否会赢,如果他赢不了,那就是我赢
// 这一整条都是用来计算我出手会不会赢
n = !((n - 1) && (n -2) && (n -3))

解决

我用了 boolean 数组,只保存前 3 个石头的答案,但是遇到像 1348820612 这种,就需要运算 1348820612 次循环,导致超出时间限制

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * 超出时间限制问题,已通过 50 个测试用例
 * @param n 石头数量
 * @return 我是否能够获胜
 */
public boolean canWinNim(int n) {
    // 我先出手,所以我直接赢了
    if (n == 1 || n == 2 || n == 3) {
        return true;
    }
    // 用来保存,前 3 个石头出手能获胜的概率
    boolean[] result = new boolean[3];
    // 只剩 1 - 3 个石头时,谁出手谁获胜
    result[0] = result[1] = result[2] = true;
    int index = 3;
    while(index < n){
        // (n - 1) && (n -2) && (n -3):这些是 n - 1,n - 2,n - 3 只剩这些石头,出手会不会赢
        // &&:我当然不会让他赢啦,只要有一个能让他输,我就拿多少石头
        // !:((n - 1) && (n -2) && (n -3)) 用来判断他是否会赢,如果他赢不了,那就是我赢
        // 这一整条都是用来计算我出手会不会赢
        boolean flag = !(result[0] && result[1] && result[2]);
        move(result, flag);
        index++;
    }
    // 最后一个就是答案了
    return result[2];
}

/**
 * 将答案往前移动
 */
public void move(boolean[] flags, boolean flag){
    flags[0] = flags[1];
    flags[1] = flags[2];
    flags[2] = flag;
}

第一次优化

这里我使用了位运算,但是还是超时限制了,不过比前面多运行成功二个例子

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * 优化版本,使用位运算
 * 超出时间限制问题,已通过 52 个测试用例
 * @param n 石头数量
 * @return 我是否能够获胜
 */
public boolean canWinNim(int n) {
    // 我先出手,所以我直接赢了
    if (n == 1 || n == 2 || n == 3) {
        return true;
    }
    // 代表到谁拿石头的输赢情况 0:代表输,1:代表赢
    // 二进制 0000 0111
    byte result = 0b111;
    // 赢的情况
    byte sureWin = 0b111;
    int index = 3;
    while(index < n){
        // & 运算:只有两位都是 1,结果才是 1
        // result & sureWin:用来判断他是否会赢,要是前面都是下一个人赢,那么我怎么拿都是输
        // (result & sureWin) == sureWin:也就是说 右边三位都是 1
        boolean myWin = !((result & sureWin) == sureWin);
        // 将所有位 左移动一位
        // 例如:result = 0000 0111,左移动一位,result = 0000 1110
        result <<= 1;
        // 如果赢了,就需要给最右边位改为 1
        if (myWin){
            result |= 1;
        }
        index++;
    }
    // 最后位是 1,代表我赢,是 0,代表我输
    return (result & 1) == 1;
}

第二次优化

思来想去,找不到好的优化方式,最后通过前面编写的方法,以找规律的方式尝试,发现只要 n % 4 ==0 就是我输

代码语言:javascript
代码运行次数:0
运行
复制
public boolean canWinNim(int n) {
    return n % 4 != 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 想法
  • 解决
    • 第一次优化
    • 第二次优化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档