464我能赢吗

Posted xxswkl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了464我能赢吗相关的知识,希望对你有一定的参考价值。

题目:在 "100 game" 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到 100 的玩家,即为胜者。如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300。

来源:https://leetcode-cn.com/problems/can-i-win/

法一:参考https://leetcode.com/problems/can-i-win/discuss/439435/Python-Minimax-Simple

思路:代码的写法极为精妙,这个题由于不易写出状态转移方程,故用备忘录的方法,python中的字典key不能为list,只能是tuple,这个题备忘录的键是剩余的元素组成的tuple,而值是输赢的两种状态,

技术图片
class Solution(object):
    def canIWin(self, maxChoosableInteger, desiredTotal):
        # 先特判,两家都不能赢时,直接返回False,即总和达不到目标值
        if maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal:
            return False
        # 如果非正,直接返回True
        if desiredTotal <= 0:
            return True
        bool_array = [i for i in range(1, maxChoosableInteger + 1)]
        print(bool_array)
        cache = {}
        def minimax(desiredTotal, visited):
            # 如果备忘录中存在该key,则直接返回
            if tuple(visited) in cache:
                return cache[tuple(visited)]
            # 如果小于等于0,说明达到目标值了,结束回溯
            if desiredTotal <= 0:
                print(visited,desiredTotal)
                return False
            for i in range(len(visited)):
                temp = visited[i]
                # 数组中除i之外的元素
                newV = visited[:i] + visited[i + 1:]
                # 如果返回的是False,表示对与visited,先拿的有必赢的方法,此时则以visited为key,值为True,
                # 如果返回的是True,表示如果此时先手拿掉数字temp,则后手可以赢,则不再遍历此时的temp,遍历下一个temp
                if not minimax(desiredTotal - temp, newV):
                    # 如果走到这一步,说明minimax(desiredTotal - temp, newV)为False,也就是说如果此时某一方在visited中
                    # 取数temp,则他必定赢,所以把visited记成True,表示一定能赢
                    cache[tuple(visited)] = True
                    # 这个return True是因为走到这一步,说明已经在visited中找到一个数可以达到目标值了,此时便结束这个函数,
                    # 因为此时已经可以保证现在拿数的这个人赢了,没必要继续遍历了,
                    # 而这一步如果可以达到目标值的话,则上一步则达不到目标值,所以返回后不再记录
                    return True
            # 如果走到这一步,说明上面for循环内的if语句都是True,也就是说visited中拿掉任意一个temp后,
            # 接下来拿的人有必赢的方法,所以这里记为False,表示必输
            cache[tuple(visited)] = False
            # 如果在此时的visited中选数的人必输,那么上一步的人就必赢,所以返回False进行记录,
            return False
        print(minimax(desiredTotal, bool_array))
        print(cache)
        return minimax(desiredTotal, bool_array)
if  __name__ == __main__:
    duixiang = Solution()
    # a = duixiang.canIWin(maxChoosableInteger = 4,desiredTotal = 7)
    a = duixiang.canIWin(maxChoosableInteger = 4,desiredTotal = 8)
    print(a)
View Code

ttt

以上是关于464我能赢吗的主要内容,如果未能解决你的问题,请参考以下文章

464 Can I Win 我能赢吗

[LeetCode] 464. Can I Win 我能赢吗

leetcode——464. 我能赢吗

LeetCode 961. 在长度 2N 的数组中找出重复 N 次的元素 / 464. 我能赢吗(博弈) / 675. 为高尔夫比赛砍树

[LeetCode] Can I Win 我能赢吗

数据结构与算法之深入解析“我能赢吗”的求解思路与算法示例