「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。
题目
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
思考
这道题是石子游戏系列题目的第三题,难度困难。结合前面做石子游戏题目的经验,我们可以考虑借助动态规划去解决该问题。
我们试着定义数组f[i],表示在剩下的区间 [i .. n-1] 的石子中,当前玩家累计可以拿得的石子数。我们用 sum(l,r) 表示第 l, l+1, ⋯, r 堆石子的的数量之和,这也就是后缀和的概念。那么,在剩下的区间 [i .. n-1] 的石子中,
如果当前玩家选择了1堆石子,则当前玩家可以拿到的石子数是 sum(i, n−1) − f[i+1]。
如果当前玩家选择了2堆石子,则当前玩家可以拿到的石子数是 sum(i, n−1) − f[i+2]。
如果当前玩家选择了3堆石子,则当前玩家可以拿到的石子数是 sum(i, n−1) − f[i+3]。
最后,f[0] 就表示 Alice 最多可以获得的石子数量,我们将 f[0] * 2 与 sum[0, n-1] 进行比较,则可以得到该题目的答案。
总而言之,这道题还是有一定的难度,需要我们去思考如何定义状态并写出状态转移方程。加油奥力给!
解答
方法一:动态规划
/**
* @author 觅迹寻踪
* @param {number[]} stoneValue
* @return {string}
*/
var stoneGameIII = function(stoneValue) {
const n = stoneValue.length
// 后缀和数组
const suffixSum = new Array(n).fill(0)
suffixSum[n - 1] = stoneValue[n - 1]
for (let i = n - 2; i >= 0; --i) {
suffixSum[i] = suffixSum[i + 1] + stoneValue[i]
}
// 数组 f
const f = new Array(n + 1).fill(0)
// 边界情况,当没有石子时,分数为 0
f[n] = 0
for (let i = n - 1; i >= 0; --i) {
let bestj = f[i + 1]
for (let j = i + 2; j <= i + 3 && j <= n; ++j) {
bestj = Math.min(bestj, f[j])
}
f[i] = suffixSum[i] - bestj
}
// f[0] * 2 与 total 进行比较
let total = 0
for (let value of stoneValue) {
total += value
}
return f[0] * 2 === total ? "Tie" : (f[0] * 2 > total ? "Alice" : "Bob")
}
复杂度分析
- 时间复杂度:O(N),其中 N 是数组 values 的长度。
- 空间复杂度:O(N)。