天天看點

LeetCode刷題——除數博弈

題目

愛麗絲和鮑勃一起玩遊戲,他們輪流行動。愛麗絲先手開局。

最初,黑闆上有一個數字 N 。在每個玩家的回合,玩家需要執行以下操作:

  • 選出任一 x,滿足 0 < x < N 且 N % x == 0 。
  • 用 N - x 替換黑闆上的數字 N 。

    如果玩家無法執行這些操作,就會輸掉遊戲。

隻有在愛麗絲在遊戲中取得勝利時才傳回 True,否則傳回 false。假設兩個玩家都以最佳狀态參與遊戲。

示例 1:

輸入:2
輸出:true
解釋:愛麗絲選擇 1,鮑勃無法進行操作。      

示例 2:

輸入:3
輸出:false
解釋:愛麗絲選擇 1,鮑勃也選擇 1,然後愛麗絲無法進行操作。      

來源:力扣(LeetCode)

連結:https://leetcode-cn.com/problems/divisor-game

思路

這個題可以用歸納法和動态規劃法來接。先看下動态規劃法。

動态規劃

将所有的小于等于 N (包含N)的解都找出來,可以基于前面的解來推後面的。

當​

​dp[i]==False​

​​,說明愛麗絲會輸,反之說明會赢。要想愛麗絲赢,就要找到一個約數,使得愛麗絲取時為​

​True​

​​,并且重新整理後的數為​

​False​

​。

class Solution(object):
    def divisorGame(self, N):
        """
        :type N: int
        :rtype: bool
        """
        dp = {} #True代表愛麗絲赢,False代表愛麗絲輸
        dp[2] = True 
        dp[1] = False
        import math
        for i in range(3,N+1):#需要包含N的情況
            dp[i] = False
            for j in range(1,int(math.sqrt(i))): #限定j的取值範圍
                if i % j == 0 and dp[i - j] == False: # 如果 選出任一j,滿足 i % j == 0,且下一個數字為False
                    dp[i] = True #找得到這麼一個j,說明i是可以獲勝的
                    break 
        return dp[N]      

歸納法

  1. 假設​

    ​N = 1​

    ​​,愛麗絲無解(選出的值​

    ​x​

    ​​必須​

    ​0 < x < N​

    ​),直接失敗;
  2. 假設​

    ​N = 2​

    ​​,愛麗絲可以選擇​

    ​x = 1​

    ​​,鮑勃​

    ​N = 2 - 1 = 1​

    ​,無解,愛麗絲獲勝;
  3. 假設​

    ​N = 3​

    ​​,愛麗絲隻可以選擇​

    ​x=1​

    ​​,鮑勃​

    ​N = 3 - 1 = 2​

    ​​,參考上面​

    ​N=2​

    ​,此時鮑勃獲勝
  4. 假設​

    ​N = 4​

    ​​,愛麗絲可以選擇​

    ​x = 1​

    ​​ 使得鮑勃遇到​

    ​N = 3​

    ​ 的情況,參考上面,則愛麗絲獲勝;
class Solution(object):
    def divisorGame(self,int N):
        return N % 2 == 0