算法 - leetcode 292 Nim Game

算法 - 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;
    }
}

 

posted @ 2019-10-01 22:02  timfruit  阅读(284)  评论(0编辑  收藏  举报