DFS--leetcode 1140石子游戏2

leetcode 1140 石子游戏2

DFS

亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。

亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。

在每个玩家的回合中,该玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。

游戏一直持续到所有石子都被拿走。

假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。

输入:piles = [2,7,9,4,4]
输出:10
解释:
如果亚历克斯在开始时拿走一堆石子,李拿走两堆,接着亚历克斯也拿走两堆。在这种情况下,亚历克斯可以拿到 2 + 4 + 4 = 10 颗石子。
如果亚历克斯在开始时拿走两堆石子,那么李就可以拿走剩下全部三堆石子。在这种情况下,亚历克斯可以拿到 2 + 7 = 9 颗石子。
所以我们返回更大的 10。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stone-game-ii

引用自leetcode刘岳大佬的解答和思路,觉得很巧妙
代码如下
以python为例:

    def stoneGameII(self, piles: List[int]) -> int:
        n = len(piles)
        s = [0]*(n+1) 
        for i in range(n-1,-1,-1):
            s[i] = s[i+1]+piles[i] #s[i]记录了第i堆到最后一堆石子的总数,这代表着某个人开始拿的时候
                                   #可以拿的理论最大值(无论他能拿多少)
        dp = {} #字典,用于记忆化搜索
        def dfs(i,M):  # i代表当前这个人拿到了第i堆,M代表当前的M值
            if (i,M) in dp: #记忆化搜索,如果这个值已经被计算过,那么直接用这个值即可
                return dp[(i,M)]
            if i>=n:  #i超过了总堆数,啥也拿不到
                return 0
            if 2*M>=(n-i):  #这说明这个人有能力一次拿完,那么对于这个人来说为了最大化收益肯定全拿走
                return s[i]
            best = 0  
            for p in range(1,2*M+1):
                best = max(best,s[i]-dfs(i+p,max(M,p))) #重点!对于每一种拿法进行遍历,拿0堆,1堆。。。
                                                        #2M堆。计算每一种可能性,并且和拿完之后的最优解
                                                        #进行比较
            dp[(i,M)]=best #记录该次的值
            return best
        return dfs(0,1)

这一题我觉得有点像人工智能导论里面说的博弈树,拿石子的双方都在尽可能的使自己的利益最大化
所以不仅要考虑自己,也要考虑拿完之后别人会怎么拿。

best = max(best,s[i]-dfs(i+p,max(M,p)))

这一行的意义就在于,比如我从第i堆开始拿,拿了p堆。那么当前剩下的最大值就是s[i],那么对方肯定会从第i+p的位置开始拿,所以减去对方拿去的,剩下就是自己理论可以拿的最大值。
这样每次双方拿的时候都可以保证自己的利益最大化。

也许后续还可以通过α-β剪枝来加快速度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值