464. Can I Win -Medium

Question

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.

给出一个最大可选数字maxChoosableInteger 和目标值desiredTotal。两个玩家轮流从1-maxChoosableInteger中选择一个数字,谁先加到或超过desiredTotal谁就赢。现在请你确定是否玩家一能够赢得比赛。假设两个玩家都发挥最优。

Example

Input:
maxChoosableInteger = 10
desiredTotal = 11

Output:
false

Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.

Solution

  • 动态规划解。玩家一赢的条件就是当他的可选项加上之前的累计值已经超过目标值。所以我们设 solve(nums_list, target)为玩家到达target是否能够赢(注意这里不确定是玩家一还是玩家二),那么如果判断玩家一是否赢呢,就是nums_list[-1] > target(可选项大于目标值,且玩家一是先选择的),当然因为是博弈问题,玩家一赢就代表玩家二输,反过来说,如果玩家二输就代表玩家一赢。所以代码如下

    
    # without memories(更易理解,不能AC)
    
    class Solution(object):
        def canIWin(self, maxChoosableInteger, desiredTotal):
            """
            :type maxChoosableInteger: int
            :type desiredTotal: int
            :rtype: bool
            """
            return self.solve(list(range(1, maxChoosableInteger + 1)), desiredTotal)
    
        def solve(self, nums_list, target):
            # 如果有可选项超出目标值,则代表玩家1赢
            if nums_list[-1] >= target: return True
    
            for index_n in range(len(nums_list)):
                # 如果玩家二赢,那么代表玩家一输,返回False
                if self.solve(nums_list[:index_n] + nums_list[index_n + 1:], target - nums_list[index_n]):
                    return False
                # 如果玩家二输,那么代表玩家一赢,返回True
                else:
                    return True
    
    # with memories
    
    class Solution(object):
        def canIWin(self, maxChoosableInteger, desiredTotal):
            """
            :type maxChoosableInteger: int
            :type desiredTotal: int
            :rtype: bool
            """
            # 因为题目规定不能重复选择数字,所以当所有数字加起来都无法超过所需总数时,两个选手均不能获胜
            if (1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal:
                return False
    
            self.memory = {}
            return self.solve(list(range(1, maxChoosableInteger + 1)), desiredTotal)
    
        def solve(self, ChoosableIntegerList, desiredTotal):
            """
            :type ChoosableIntegerList: List[int]
            :type desiredTotal: int
            :rtype: bool
            """
            # 如果当前状态已经计算过,则直接取出
            if tuple(ChoosableIntegerList) in self.memory:
                return self.memory[tuple(ChoosableIntegerList)]
    
            if desiredTotal <= ChoosableIntegerList[-1]:
                return True
    
            for index, choosableInteger in enumerate(ChoosableIntegerList):
                if not self.solve(ChoosableIntegerList[:index] + ChoosableIntegerList[index + 1:], desiredTotal - choosableInteger):
                    # 保存当前状态player1获胜
                    self.memory[tuple(ChoosableIntegerList)] = True
                    return True
            # 保存当前状态player1败北
            self.memory[tuple(ChoosableIntegerList)] = False
            return False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值