Leetcode 1140:石子游戏 II(超详细的解法!!!)

亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 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。 

提示:

  • 1 <= piles.length <= 100
  • 1 <= piles[i] <= 10 ^ 4

解题思路

这个问题首先我们可以想到通过dfs来做,我们定义函数 f ( i , M ) f(i,M) f(i,M),表示在piles[i:]中以[1,2*M]为取值范围可以取的最多石子数的差(看后面解释),那么

  • f ( i , M ) = m a x ( s u m ( p i l e s [ i : i + x ] ) − f ( i + x , m a x ( M , x ) ) )    ( 1 ≤ x ≤ 2 ∗ M ) f(i,M)=max(sum(piles[i:i+x])-f(i+x,max(M, x)))\ \ (1\leq x \leq 2*M) f(i,M)=max(sum(piles[i:i+x])f(i+x,max(M,x)))  (1x2M)

稍微解释一下,我们上面这个式子实际上得到的是以亚历克斯起手最后两人得到的石子数的最大差值,那么最后我们需要计算亚历克斯能有多少石子的话,我们就需要将结果加上sum(piles)然后除上2即可。

from functools import lru_cache
class Solution:
    def stoneGameII(self, piles: List[int]) -> int:
        n = len(piles)
        @lru_cache(None)
        def dfs(d, x):
            if d + 2*x >= n:
                return sum(piles[d:])
            res = float("-inf")
            for i in range(1, 2*x+1):
                res = max(res, sum(piles[d:d + i]) - dfs(d + i, max(i, x)))
            return res
        return (sum(piles) + dfs(0, 1))//2

我们当然可以定义函数 f ( i , M ) f(i,M) f(i,M),表示在piles[i:]中以[1,2*M]为取值范围可以取的最多石子数,那么此时的方程需要变化一下

  • f ( i , M ) = s u m ( p i l e s [ i : ] ) − m i n ( f ( i + x , m a x ( M , x ) ) )    ( 1 ≤ x ≤ 2 ∗ M ) f(i,M)=sum(piles[i:])-min(f(i+x,max(M, x)))\ \ (1\leq x \leq 2*M) f(i,M)=sum(piles[i:])min(f(i+x,max(M,x)))  (1x2M)

也就是说,我们希望亚历克斯尽可能多拿的话,那么我们就希望李少拿。亚历克斯最多拿sum(piles[i:])个石子,而李最少拿min(f(i+x,max(M, x)))个石子,最后亚历克斯实际上可以最多拿二者之差个石子。

from functools import lru_cache
class Solution:
    def stoneGameII(self, piles: List[int]) -> int:
        n = len(piles)
        for i in range(n - 2, -1, -1):
            piles[i] += piles[i + 1]

        @lru_cache(None)
        def dfs(i, m):
            if i + 2 * m >= n: 
                return piles[i]
            return piles[i] - min(dfs(i + x, max(m, x)) for x in range(1, 2 * m + 1))
        return dfs(0, 1)

reference:

https://leetcode.com/problems/stone-game-ii/discuss/345230/Python-DP-Solution

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)