算法 - leetcode 292 Nim Game
一丶题目
你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。
你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。
输入: 4
输出: false
解释: 如果堆中有 4 块石头,那么你永远不会赢得比赛;
因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。
二丶思路
1) 先尝试使用暴力解决--递归
class Solution {
public boolean canWinNim(int n) {
if(n<=0){
return false;
}
if(n<=3){
return true;
}
if(!friendCanWinNim(n-1)){ //拿1块
return true;
}
if(!friendCanWinNim(n-2)){ //拿2块
return true;
}
if(!friendCanWinNim(n-3)){ //拿3块
return true;
}
return false;
}
//朋友拿, 朋友是否可以赢
private boolean friendCanWinNim(int n){
if(n<=0){
return false;
}
if(n<=3){
return true;
}
if(!canWinNim(n-1)){ //拿1块
return true;
}
if(!canWinNim(n-2)){ //拿2块
return true;
}
if(!canWinNim(n-3)){ //拿3块
return true;
}
return false;
}
}
2) 出现超时的现象,缓存中间结果
class Solution {
Map<Integer, Boolean> resultMap =new HashMap<Integer, Boolean>(); //结果
public boolean canWinNim(int n) {
if(n<=0){
return false;
}
if(n<=3){
return true;
}
if(results[n]!=null){
return results[n];
}
boolean result=false;
if(!friendCanWinNim(n-1)){ //拿1块
result=true;
resultMap.put(n, result);return result;
}
if(!friendCanWinNim(n-2)){ //拿2块
result=true;
resultMap.put(n, result);return result;
}
if(!friendCanWinNim(n-3)){ //拿3块
result=true;
resultMap.put(n, result);return result;
}
result=false;
resultMap.put(n, result);return result;
}
//朋友拿, 朋友是否可以赢
private boolean friendCanWinNim(int n){
if(n<=0){
return false;
}
if(n<=3){
return true;
}
if(!canWinNim(n-1)){ //拿1块
return true;
}
if(!canWinNim(n-2)){ //拿2块
return true;
}
if(!canWinNim(n-3)){ //拿3块
return true;
}
return false;
}
}
3) 递归过大, 出现栈溢出异常, 将递归改成for循环 (类似动态规划)
class Solution {
Map<Integer, Boolean> resultMap =new HashMap<Integer, Boolean>(); //缓存先手的结果
public boolean canWinNim(int n) {
if(n<=0){
return false;
}
// 初始化
resultMap.put(1, true);
resultMap.put(2, true);
resultMap.put(3, true);
boolean result1, result2, result3;
for(int i=4;i<=n;i++){
// 拿1块
result1=resultMap.get(i-1);
// 拿2块
result2=resultMap.get(i-2);
// 拿3块
result3=resultMap.get(i-3);
if(!result1 || !result2 || !result3){
resultMap.put(i, true);
}else {
resultMap.put(i, false);
}
System.out.println(i+" -> "+resultMap.get(i));
}
return resultMap.get(n);
}
}
4) 仍然出现超时, (打印中间结果可发现规律 T_T 这里看了别人的解释)
class Solution {
public boolean canWinNim(int n) {
//规律题
// 3+1 必输
return n%(3+1)!=0;
}
}