LeetCode 题解工作台

石子游戏 VIII

Alice 和 Bob 玩一个游戏,两人轮流操作, Alice 先手 。 总共有 n 个石子排成一行。轮到某个玩家的回合时,如果石子的数目 大于 1 ,他将执行以下操作: 选择一个整数 x > 1 ,并且 移除 最左边的 x 个石子。 将 移除 的石子价值之 和 累加到该玩家的分数中。 将一个 新的…

category

5

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

根据题目描述,每次取走最左边的 个石子,把它们的和加到自己的分数中,然后把一个价值为这个和的石子放在最左边,相当于把这 个石子合并成了一个价值为这个和的石子,前缀和不变。 我们可以用一个长度为 的前缀和数组 来表示数组 的前缀和,其中 表示 的元素和。

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

Alice 和 Bob 玩一个游戏,两人轮流操作, Alice 先手 。

总共有 n 个石子排成一行。轮到某个玩家的回合时,如果石子的数目 大于 1 ,他将执行以下操作:

  1. 选择一个整数 x > 1 ,并且 移除 最左边的 x 个石子。
  2.  移除 的石子价值之  累加到该玩家的分数中。
  3. 将一个 新的石子 放在最左边,且新石子的值为被移除石子值之和。

当只剩下 一个 石子时,游戏结束。

Alice 和 Bob 的 分数之差 为 (Alice 的分数 - Bob 的分数) 。 Alice 的目标是 最大化 分数差,Bob 的目标是 最小化 分数差。

给你一个长度为 n 的整数数组 stones ,其中 stones[i] 是 从左边起 第 i 个石子的价值。请你返回在双方都采用 最优 策略的情况下,Alice 和 Bob 的 分数之差

 

示例 1:

输入:stones = [-1,2,-3,4,-5]
输出:5
解释:
- Alice 移除最左边的 4 个石子,得分增加 (-1) + 2 + (-3) + 4 = 2 ,并且将一个价值为 2 的石子放在最左边。stones = [2,-5] 。
- Bob 移除最左边的 2 个石子,得分增加 2 + (-5) = -3 ,并且将一个价值为 -3 的石子放在最左边。stones = [-3] 。
两者分数之差为 2 - (-3) = 5 。

示例 2:

输入:stones = [7,-6,5,10,5,-2,-6]
输出:13
解释:
- Alice 移除所有石子,得分增加 7 + (-6) + 5 + 10 + 5 + (-2) + (-6) = 13 ,并且将一个价值为 13 的石子放在最左边。stones = [13] 。
两者分数之差为 13 - 0 = 13 。

示例 3:

输入:stones = [-10,-12]
输出:-22
解释:
- Alice 只有一种操作,就是移除所有石子。得分增加 (-10) + (-12) = -22 ,并且将一个价值为 -22 的石子放在最左边。stones = [-22] 。
两者分数之差为 (-22) - 0 = -22 。

 

提示:

  • n == stones.length
  • 2 <= n <= 105
  • -104 <= stones[i] <= 104
lightbulb

解题思路

方法一:前缀和 + 记忆化搜索

根据题目描述,每次取走最左边的 xx 个石子,把它们的和加到自己的分数中,然后把一个价值为这个和的石子放在最左边,相当于把这 xx 个石子合并成了一个价值为这个和的石子,前缀和不变。

我们可以用一个长度为 nn 的前缀和数组 ss 来表示数组 stonesstones 的前缀和,其中 s[i]s[i] 表示 stones[0..i]stones[0..i] 的元素和。

接下来,我们设计一个函数 dfs(i)dfs(i),表示当前从 stones[i:]stones[i:] 中取石子,返回当前玩家能得到的最大分数差。

函数 dfs(i)dfs(i) 的执行过程如下:

  • 如果 in1i \geq n - 1,说明当前只能取走全部石子,因此返回 s[n1]s[n - 1]
  • 否则,我们可以选择从 stones[i+1:]stones[i + 1:] 中取走全部石子,得到的分数差为 dfs(i+1)dfs(i + 1);也可以选择取走 stones[:i]stones[:i] 的石子,得到的分数差为 s[i]dfs(i+1)s[i] - dfs(i + 1)。我们取两种情况中的最大值,即为当前玩家能得到的最大分数差。

最终,我们可以得到 Alice 和 Bob 的分数之差为 dfs(1)dfs(1),即 AliceAlice 必须从 stones[1:]stones[1:] 中取石子开始游戏。

为了避免重复计算,我们可以使用记忆化搜索。

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)。其中 nn 为数组 stonesstones 的长度。

1
2
3
4
5
6
7
8
9
10
11
class Solution:
    def stoneGameVIII(self, stones: List[int]) -> int:
        @cache
        def dfs(i: int) -> int:
            if i >= len(stones) - 1:
                return s[-1]
            return max(dfs(i + 1), s[i] - dfs(i + 1))

        s = list(accumulate(stones))
        return dfs(1)
speed

复杂度分析

指标
时间complexity is O(n) due to single pass DP using prefix sums. Space complexity is O(n) if storing full DP array or O(1) if optimized to keep only previous state.
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Expecting prefix sum usage for move sums

  • question_mark

    Checking understanding of state transition DP

  • question_mark

    Looking for handling large n efficiently without nested loops

warning

常见陷阱

外企场景
  • error

    Ignoring that at least two stones must be removed per move

  • error

    Incorrectly tracking score difference instead of individual scores

  • error

    Recomputing prefix sums in inner loops causing TLE

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Changing score function to product instead of sum

  • arrow_right_alt

    Allowing removal of exactly k stones instead of at least two

  • arrow_right_alt

    Scoring based on alternating stone positions rather than prefix sums

help

常见问题

外企场景

石子游戏 VIII题解:状态·转移·动态规划 | LeetCode #1872 困难