目录
【算法题解】石子游戏 VII:最大得分差的博弈问题
题目描述
在「石子游戏 VII」中,爱丽丝和鲍勃轮流从一排石子中取走石子,爱丽丝先手。共有 n 块石子,排成一排,每块石子有一个整数值。两人轮流行动,每次只能从当前石子行的最左端或最右端拿走一个石子。
关键规则:
- 拿走石子后,该玩家获得的得分等于剩余石子值之和。
- 当没有石子可以拿时游戏结束。
- 两人都采用最优策略,爱丽丝希望最大化最终得分差(爱丽丝得分 - 鲍勃得分),而鲍勃想尽量减少这个差值。
请你计算,在双方都最优的情况下,最终的得分差是多少。
题目举例(示意)
假设石子行是 [5, 3, 1, 4, 2],
- 初始总和为
5 + 3 + 1 + 4 + 2 = 15。 - 爱丽丝先手,可以取左边的
5或右边的2,但拿走后得分是剩余石子的和,不是石子本身的值。 - 如果爱丽丝拿走左边的
5,剩余石子为[3, 1, 4, 2],得分为10。 - 如果拿走右边的
2,剩余石子为[5, 3, 1, 4],得分为13。
然后鲍勃继续选,双方都尽力最大化各自利益,最终求出差值。
解题分析
这是一个典型的博弈问题,要求双方在最优策略下对差值的最大化(或最小化)。
关键点在于:
- 当前玩家拿走的是石子,但获得的是剩余石子的值之和。
- 这意味着玩家的得分依赖于剩下的石子和,与常见“拿走石子直接得分”不同。
- 玩家选择后,局面缩小,转由对手进行同样的选择。
- 我们希望计算最终两人得分的差值。
解决思路:动态规划
定义状态
令 dp[i][j] 表示当前玩家面对区间 [i, j] 的石子时,能获得的最大得分差。这里的得分差是“当前玩家得分 - 另一个玩家得分”。
状态转移
当前玩家可以从区间两端取石子:
- 取左端
stones[i]:
玩家获得的得分是剩余石子和,即区间[i+1, j]的和sum(i+1,j)。
之后剩下[i+1, j],轮到对手操作,对手能取得的最大差值是dp[i+1][j],这对当前玩家来说是负数(对手得分 - 当前玩家得分)。
因此取左端的净得分差为:
sum(i+1,j) - dp[i+1][j] - 取右端
stones[j]:
同理,得分为sum(i, j-1),后续差值为dp[i][j-1],净得分差:
sum(i, j-1) - dp[i][j-1]
最终选择两者中较大的作为 dp[i][j]:
dp[i,j]=max(sum(i+1,j)−dp[i+1,j],sum(i,j−1)−dp[i,j−1])dp[i,j] = \max\big( sum(i+1,j) - dp[i+1,j],\quad sum(i,j-1) - dp[i,j-1] \big)
边界条件
当区间只剩一个石子,即 i == j,当前玩家拿走这唯一石子后,没有剩余石子,得分为0:
dp[i,i]=0dp[i,i] = 0
预处理优化
计算区间和 sum(i,j) 可以通过前缀和数组 prefix_sum 优化,使得求区间和变成 O(1) 时间。
代码实现
from typing import List
class Solution:
def stoneGameVII(self, stones: List[int]) -> int:
n = len(stones)
# 计算前缀和,prefix_sum[i] 表示 stones[0:i] 的和
prefix_sum = [0] * (n+1)
for i in range(n):
prefix_sum[i+1] = prefix_sum[i] + stones[i]
def get_sum(i, j):
return prefix_sum[j+1] - prefix_sum[i]
dp = [[0] * n for _ in range(n)]
# 处理区间长度从2到n
for length in range(2, n+1):
for i in range(n - length + 1):
j = i + length - 1
# 取左边石子
left_choice = get_sum(i+1, j) - dp[i+1][j]
# 取右边石子
right_choice = get_sum(i, j-1) - dp[i][j-1]
dp[i][j] = max(left_choice, right_choice)
return dp[0][n-1]
复杂度分析
- 时间复杂度:
外层循环枚举区间长度O(n),内层循环遍历起点O(n),状态转移常数时间,整体O(n^2)。 - 空间复杂度:
使用了dp二维数组和prefix_sum数组,空间为O(n^2)。
示例说明
以输入 [5, 3, 1, 4, 2] 为例:
- 总和为 15。
- 爱丽丝先手,选择拿左边
5或右边2。 - 拿左边剩余
[3,1,4,2],得分为 10。拿右边剩余[5,3,1,4],得分为 13。 - 然后轮到鲍勃,他会做出最佳反制选择。
- 最终 dp 计算得到爱丽丝和鲍勃得分差最大为
8(具体过程依赖递归展开和状态转移)。
小结
- 这题是一类典型的两人博弈动态规划问题。
- 状态设计核心是「当前玩家最大得分差」,利用「取石后剩余石子和减去对手最优差值」构建转移。
- 使用前缀和优化区间和计算。
- 代码简洁且高效,适合掌握博弈 DP 模板。
石子游戏 VII:博弈问题的动态规划解法
3036

被折叠的 条评论
为什么被折叠?



