292_Nim Game

292_Nim Game

一、题目详情

  题目的意思是给定石头的数量n,双方每次只能拿1-3个石头,己方先手,问在给定n后,自己能不能获胜。

二、解题方法

第一种方法(Time Limit Exceeded)

  一开始拿到题目,我的第一想法是使用博弈树的思想解题,当节点状态是己方行动&&石子数量<=3获胜,当节点是对方行动&&石子数量<=3失败。因为双方都足够聪明,所以要在对手每一步取走1个,2个,3个石子都必须取胜。代码如下:

class Solution {
    public boolean canWinNim(int n) {
        return canWinNimHelper(n,true);
    }
    //isSelf==true表示己方行动,false表示对手行动
    public boolean canWinNimHelper(int n,boolean isSelf){
        if(n<=3&&isSelf) return true;
        if(n<=3&&!isSelf) return false;
        if(isSelf)
            return canWinNimHelper(n-1,!isSelf)
                || canWinNimHelper(n-2,!isSelf)
                || canWinNimHelper(n-3,!isSelf);
        else
            return canWinNimHelper(n-1,!isSelf)
                && canWinNimHelper(n-2,!isSelf)
                && canWinNimHelper(n-3,!isSelf);
    }
}

  不出意料这种方法Time Limit Exceeded.

第二种方法(Memory Limit Exceeded)

  因为上面这种算法超时,我观察了一下该算法,可以使用动态规划的思想解决超时问题。算法具体为:现在求得是由n个石子是否能赢,则只要拿走1个棋子或者两个棋子或者三个棋子后,对方会输就行(己方和对方是一样的,dp[i]只表示现在行动的那方能不能赢)。所以就有:
dp[i] = !(dp[i-1]&&dp[i-2]&&dp[i-3])

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

  这个我以为可以accept,但是题目给的数据太大,建立dp数组太耗空间,但是这里可以改进一下:因为dp时只需要i的i-1,i-2,i-3的值,所以可以只保留这三个,这样就解决了内存的问题。

第三种方法(Accept)

  这种方法比较特殊。只要n不能被4整除自己就能赢。
解释:我们可以知道当n=1,2,3时我们可以赢,当n=4时我们会输。当n=5,6,7时,我们可以对应的拿掉1,2,3个石子让对方行动时只有4个棋子,我们就能赢。n=8时,我们会输。以此类推,只要n不能被4整除自己就能赢。

class Solution {
    public boolean canWinNim(int n) {
        return !(n%4==0);
    } 
}

猜你喜欢

转载自www.cnblogs.com/Mrfanl/p/11695378.html