Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue
给出。
Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。
每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。
假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 “Alice” *,*Bob 赢了就返回 *“Bob”,*平局(分数相同)返回 “Tie” 。
示例 1:
输入:values = [1,2,3,7]
输出:"Bob"
解释:Alice 总是会输,她的最佳选择是拿走前三堆,得分变成 6 。但是 Bob 的得分为 7,Bob 获胜。
示例 2:
输入:values = [1,2,3,-9]
输出:"Alice"
解释:Alice 要想获胜就必须在第一个回合拿走前三堆石子,给 Bob 留下负分。
如果 Alice 只拿走第一堆,那么她的得分为 1,接下来 Bob 拿走第二、三堆,得分为 5 。之后 Alice 只能拿到分数 -9 的石子堆,输掉比赛。
如果 Alice 拿走前两堆,那么她的得分为 3,接下来 Bob 拿走第三堆,得分为 3 。之后 Alice 只能拿到分数 -9 的石子堆,同样会输掉比赛。
注意,他们都应该采取 最优策略 ,所以在这里 Alice 将选择能够使她获胜的方案。
示例 3:
输入:values = [1,2,3,6]
输出:"Tie"
解释:Alice 无法赢得比赛。如果她决定选择前三堆,她可以以平局结束比赛,否则她就会输。
示例 4:
输入:values = [1,2,3,-1,-2,-3,7]
输出:"Alice"
示例 5:
输入:values = [-1,-2,-3]
输出:"Tie"
提示:
1 <= values.length <= 50000
-1000 <= values[i] <= 1000
解题思路
这个问题和Leetcode 1140:石子游戏 II,按照之前问题的第一种思路(也就是计算Bob
和Alice
两个人可以取的最大差值)。定义函数 f ( i ) f(i) f(i)表示最大差值,那么:
from functools import lru_cache
class Solution:
def stoneGameIII(self, stoneValue: List[int]) -> str:
n = len(stoneValue)
@lru_cache(None)
def dfs(d):
if d == n: return 0
res = float("-inf")
for i in range(1, 4):
if d + i <= n:
res = max(res, sum(stoneValue[d:d + i]) - dfs(d + i))
return res
dif = dfs(0)
if dif == 0: return "Tie"
return "Alice" if dif > 0 else "Bob"
也可以参考第二种定义方式,定义 f ( i ) f(i) f(i)表示在stoneValue[i:]
中对方取[1,3]
堆石子后可获得的最多石子数,那么:
也就是对于Alice
来说他所获得的石子数就是sum(stoneValue)
减去Bob
获得的石子数,此时对于Alice
来说希望Bob
获得的石子数尽量少。
from functools import lru_cache
class Solution:
def stoneGameIII(self, stoneValue: List[int]) -> str:
n, sum_all = len(stoneValue), stoneValue[-1]
for i in range(n - 2, -1, -1):
sum_all += stoneValue[i]
stoneValue[i] += stoneValue[i + 1]
@lru_cache(None)
def dfs(i):
if i == n: return 0
return stoneValue[i] - min(dfs(i + x) for x in range(1, 4) if i + x <= n)
Alice = dfs(0)
if Alice * 2 == sum_all: return "Tie"
return "Alice" if Alice * 2 > sum_all else "Bob"
上面这个代码会出现maximum recursion depth
错误,可以采用动态规划的写法。
class Solution:
def stoneGameIII(self, A: List[int]) -> str:
n, sum_all = len(A), 0
dp = [0] * (n + 1)
for i in range(n - 1, -1, -1):
sum_all += A[i]
dp[i] = sum_all - min(dp[i + k] for k in range(1, 4) if i + k <= n)
if dp[0] * 2 == sum_all: return "Tie"
return "Alice" if dp[0] * 2 > sum_all else "Bob"
这个代码还可以优化,可以将空间复杂度优化到O(1)
。
class Solution:
def stoneGameIII(self, A: List[int]) -> str:
dp, n, sum_all = [0] * 3, len(A), 0
for i in range(n - 1, -1, -1):
sum_all += A[i]
dp[i % 3] = sum_all - min(dp[(i + k) % 3] for k in range(1, 4))
if dp[0] * 2 == sum_all: return "Tie"
return "Alice" if dp[0] * 2 > sum_all else "Bob"
非常的简洁优雅!!!
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!