likes
comments
collection
share

夯实算法-Nim游戏

作者站长头像
站长
· 阅读数 35

题目:LeetCode

你和你的朋友,两个人一起玩 Nim 游戏:

桌子上有一堆石头。 你们轮流进行自己的回合, 你作为先手 。 每一回合,轮到的人拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。 假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

示例 1:

输入:n = 4
输出:false 
解释:以下是可能的结果:
1. 移除1颗石头。你的朋友移走了3块石头,包括最后一块。你的朋友赢了。
2. 移除2个石子。你的朋友移走2块石头,包括最后一块。你的朋友赢了。
3.你移走3颗石子。你的朋友移走了最后一块石头。你的朋友赢了。
在所有结果中,你的朋友是赢家。

示例 2:

输入:n = 1
输出:true

示例 3:

输入:n = 2
输出:true

解题思路

从第一步开始分析,对于当前先手来说:

  • 如果剩余的石头为0,则当前先手必败。
  • 如果剩余石头为1-3,则当前先手必胜。

但现在有n个石头,如何判断先手是必胜还是必败?其实画个图很容易分析:

  • 当前先手对应n个石头,后手就对应n-1、n-2、n-3三种集合的石头
  • 每个集合一定都会对应必胜或者必败
  • 因此可以得到这样一个递推关系:

只有当n-1、n-2、n-3三种集合都必胜时,n对应的集合才必败。因为不管n走哪条路,都一定对应着后手必胜,也就是对应着先手必败。

而但凡后手对应的n-1、n-2、n-3三种集合有一种对应的是必败,先手都一定是必胜。因为玩家是绝对聪明的,一定会走让后手必败的路线。

代码实现

public boolean canWinNim(int n) {
    boolean[] dp = new boolean[4];
    for(int i = 1; i <= n; i++) {
        boolean ret = true;
        ret &= dp[(i - 1) % 4];
        if(i >= 2) {
            ret &= dp[(i - 2) % 4];
        }
        if(i >= 3) {
            ret &= dp[(i - 3) % 4];
        }
        dp[i % 4] = !ret;
    }
    return dp[n % 4];
}

复杂度分析

  • 空间复杂度:O(1)O(1)O(1)
  • 时间复杂度:O(N)O(N)O(N)
转载自:https://juejin.cn/post/7171458839315742728
评论
请登录