LeetCode 464 - Can I Win - Medium (Python)

In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.

What if we change the game so that players cannot re-use integers?

For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.

Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.

You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.

Example

Input:
maxChoosableInteger = 10
desiredTotal = 11

Output:
false

需要注意的是这题的数字是不能重复使用的。以及超出desiredTotal也算赢。
思路: dfs。
1) dfs跳出的条件是可以选的nums中最大的数字也比此时的target大。比如现在可以选的数字有[1,2,3,6,7,8]。target是7,那么我们可以直接出8就赢得比赛了。
2) dfs memorization 剪枝别忘了。

步骤: 1 构造nums,即所可以选的全部num的list。nums = [i for i in range(1, maxChoosableInteger+1)].
2 在进入dfs之前,可以预判˜˜。如果sum(nums) 大于desiredInteger,那么我们可以直接return False。 (因为所有的数字都只能用一次!!)如果sum(nums)刚好等于desiredInteger,
并且len(nums) %2 != 0, 那么我们可以直接返回True。
3 构造一个self.seen = {}来记录所有出现过的nums的组合,以及其所对应的值(True or False)
4 进入dfs。 def can_win(self, nums, desiredTotal)
在dfs中,我们首先判断nums[-1],也就是此时最大的数字是否比我们desiredTotal大,大的话,return True. 第二步判断这个nums是否有出现在self.seen过。如果有的话,
return self.seen[tuple(nums)].(需要注意的是,因为nums是一个list,我们需要转成tuple之后才可以转成tuple保存在self.seen中。)
如果上面两个判断都不符合的话,那就开始对nums遍历。假设我们选的是nums[i],意味着进入下一步dfs循环时候的nums是nums[:i] + nums[i+1:], desiredTotal变成了desiredTotal - nums[i].
并且需要注意的是,这一步的循环是对于对手来说的,那么如果我们得到的下一步循环的结果是True,意味着这一步的结果是False(因为对手成功了,对我们来说就是失败)。如果有任何一种下一步
的情况是False的话,意味着我们这时候的结果是True的。那我们把这个状态下的结果就可以保存在self.seen中,同时直接return True。
如果所有的num都遍历过了,我们还是不能找到一个True的状态,那么也就把这个状态保存,值为False。同时return False。
class Solution:
    def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:
        if maxChoosableInteger >= desiredTotal:
            return True 
        nums = [i for i in range(1, maxChoosableInteger+1)]
        if sum(nums) < desiredTotal:
            return False 
        if sum(nums) == desiredTotal and len(nums) % 2 != 0:
            return True 
        
        self.seen = {}
        
        return self.can_win(nums, desiredTotal)
    
    def can_win(self, nums, target):
        if nums[-1] >= target:
            return True 
        if tuple(nums) in self.seen:
            return self.seen[tuple(nums)]
        
        for i in range(len(nums)):
            if not self.can_win(nums[:i] + nums[i+1:], target-nums[i]):
                self.seen[tuple(nums)] = True 
                return True 
        self.seen[tuple(nums)] = False 
        return False 


猜你喜欢

转载自www.cnblogs.com/sky37/p/12239515.html