问题描述
你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3
块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。
你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。
示例
输入: 4
输出: false
解释: 如果堆中有 4 块石头,那么你永远不会赢得比赛;
因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。
思路
这题一开始蒙圈了。后来发现这是个dp问题。即,状态转移方程为dp[i] = dp[i-3]&&dp[i-2]&&dp[i-1]
。为啥呢?设想一下。你可以拿1块,拿两块,拿三块。当你拿走一块的时候,你朋友就要拿dp[i-1]
,你拿走两块你朋友就要拿dp[i-2]
,你拿走三块你朋友就要拿dp[i-3]
. 所以说你能不能赢要看你拿走之后你朋友能不能赢。
想清楚这点之后,我就开始用dp写(方法一)。结果TLE了。我日???O(n)
的dp你给我TLE?
看来有更简单的办法。多写了几项看了看:
如果前面的3个都是true
,那么dp[i]
肯定是false.
如果前面3个有false
,那么dp[i]
肯定就是true.
好像发现了什么:
1,2,3赢了。
4,输了
5,赢了
6,赢了
7,赢了
8,输了
好像是4个一组的。如果n%4
之后等于0,则输了。否则就是赢了。(方法二)
方法一
Java版
public boolean canWinNim1(int n) {
if(n < 4){
return true;
}
boolean[] res = new boolean[n+1];
res[1] = true;
res[2] = true;
res[3] = true;
for(int i = 4; i <= n; i++){
boolean win = false;
for(int j = 1; j <= 3; j++){
if(!res[i-j]){
win = true;
break;
}
}
res[i] = win;
}
return res[n];
}
方法二
Java版
class Solution {
public boolean canWinNim(int n){
return n%4 > 0;
}
}