zoukankan      html  css  js  c++  java
  • 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,而值是输赢的两种状态,值有两种状态True和False,必赢和必输。每次保留剩余的没有取的数字,如果在list1中拿掉某个数字后满足条件了,则将该list1记入备忘录中,结束本次回溯,然后便不再遍历list1中的其它元素,因为面对list1的时候,已经有必胜的方法了,故结束回溯,注意这里结束回溯的时候返回True,这里的return True有两种情况,一种是最外面循环的return True,这时程序结束返回了True,另一种是里面的return True,比如list1有a b c d e五个数字,list2中是b c d e四个,首先遍历list1中的a,再遍历list2,如果list2中取了某个数后可以达到目标值,这时以list2为键,值为True记入备忘录中,再返回True,表示面对list2玩家有必胜的方法,则面对list1的玩家,选a必输,故返回True后,if not minimax(desiredTotal - temp, newV) 结束list1中对a的循环,继续遍历b,如果list1中对a b c d e的遍历都返回了True,说明玩家面对list1时,无论选哪个都是输,所以这时cache[tuple(visited)] = False,以list1为键,值为False记入备忘录中,而list1又是另一个更大的list拿掉一个元素x后剩下的,而玩家在list1中选哪个都是输,上一个玩家面对 x + list1时必定赢,所以返回False在备忘录中记录键值。

            minimax()函数有四个return作用各不相同,第一个是备忘录中存在该键值对,故直接返回以前记录的结果。第二个是去了某个数后,达到目标值了返回False,记录键值对。第三个是上一步记录的键值对都是True,则当前的list没有继续遍历的必要,返回True,让上一级继续遍历。第四个是上一步把当前遍历的list记到了备忘录中,值为False,表示必输,则上一级有必胜的方法,于是返回False,让上一级记为True。

            这个题要返回的是先手的输赢状态,而回溯的最上层就是遍历list中的每个元素,则回溯的最上层返回的结果即先手的输赢状态。

    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

  • 相关阅读:
    基于BGP/EVPN控制平面的VXLAN anycast-VTEP anycast-gateway基本配置
    NetworkManager配置VRF
    IBGP Segment Routing AIGP属性
    EBGP segment routing
    CentOS8创建网桥
    F5 HTTP response body rewrite
    OSPF Segment Routing和MPLS基本配置
    L2TP 和 IPsec over L2TP
    nmap
    LINUX DNS客户端 解析域名慢的问题。
  • 原文地址:https://www.cnblogs.com/xxswkl/p/12268131.html
Copyright © 2011-2022 走看看