夯实算法-Nim游戏
题目: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