爱丽丝和鲍勃继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]
。游戏以谁手中的石子最多来决出胜负。
爱丽丝和鲍勃轮流进行,爱丽丝先开始。最初,M = 1
。
在每个玩家的回合中,该玩家可以拿走剩下的 前 X
堆的所有石子,其中 1 <= X <= 2M
。然后,令 M = max(M, X)
。
游戏一直持续到所有石子都被拿走。
假设爱丽丝和鲍勃都发挥出最佳水平,返回爱丽丝可以得到的最大数量的石头。
示例 1:
输入:piles = [2,7,9,4,4] 输出:10 解释:如果一开始Alice取了一堆,Bob取了两堆,然后Alice再取两堆。爱丽丝可以得到2 + 4 + 4 = 10堆。如果Alice一开始拿走了两堆,那么Bob可以拿走剩下的三堆。在这种情况下,Alice得到2 + 7 = 9堆。返回10,因为它更大。
示例 2:
输入:piles = [1,2,3,4,5,100] 输出:104
提示:
1 <= piles.length <= 100
1 <= piles[i] <= 104
由于玩家每次可以拿走前 piles
的前
然后我们设计一个函数 piles
的下标
函数
- 如果当前轮到的人可以拿走剩下的所有石子,能够拿到的最大石子数为
$s[n] - s[i]$ ; - 否则,当前轮到的人可以拿走剩下的前
$x$ 堆的所有石子,其中$1 \leq x \leq 2m$ ,能够拿到的最大石子数为$s[n] - s[i] - dfs(i + x, max(m, x))$ 。也即是说,当前轮的人能够拿到的石子数为当前剩下的所有石子数减去下一轮对手能够拿到的石子数。我们需要枚举所有的$x$ ,取其中的最大值作为函数$dfs(i, m)$ 的返回值。
为了避免重复计算,我们可以使用记忆化搜索。
最后,我们返回将
时间复杂度为 piles
的长度。
class Solution:
def stoneGameII(self, piles: List[int]) -> int:
@cache
def dfs(i, m):
if m * 2 >= n - i:
return s[n] - s[i]
return max(
s[n] - s[i] - dfs(i + x, max(m, x)) for x in range(1, m << 1 | 1)
)
n = len(piles)
s = list(accumulate(piles, initial=0))
return dfs(0, 1)
class Solution {
private int[] s;
private Integer[][] f;
private int n;
public int stoneGameII(int[] piles) {
n = piles.length;
s = new int[n + 1];
f = new Integer[n][n + 1];
for (int i = 0; i < n; ++i) {
s[i + 1] = s[i] + piles[i];
}
return dfs(0, 1);
}
private int dfs(int i, int m) {
if (m * 2 >= n - i) {
return s[n] - s[i];
}
if (f[i][m] != null) {
return f[i][m];
}
int res = 0;
for (int x = 1; x <= m * 2; ++x) {
res = Math.max(res, s[n] - s[i] - dfs(i + x, Math.max(m, x)));
}
return f[i][m] = res;
}
}
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
int s[n + 1];
s[0] = 0;
for (int i = 0; i < n; ++i) {
s[i + 1] = s[i] + piles[i];
}
int f[n][n + 1];
memset(f, 0, sizeof f);
function<int(int, int)> dfs = [&](int i, int m) -> int {
if (m * 2 >= n - i) {
return s[n] - s[i];
}
if (f[i][m]) {
return f[i][m];
}
int res = 0;
for (int x = 1; x <= m << 1; ++x) {
res = max(res, s[n] - s[i] - dfs(i + x, max(x, m)));
}
return f[i][m] = res;
};
return dfs(0, 1);
}
};
func stoneGameII(piles []int) int {
n := len(piles)
s := make([]int, n+1)
f := make([][]int, n+1)
for i, x := range piles {
s[i+1] = s[i] + x
f[i] = make([]int, n+1)
}
var dfs func(i, m int) int
dfs = func(i, m int) int {
if m*2 >= n-i {
return s[n] - s[i]
}
if f[i][m] > 0 {
return f[i][m]
}
f[i][m] = 0
for x := 1; x <= m<<1; x++ {
f[i][m] = max(f[i][m], s[n]-s[i]-dfs(i+x, max(m, x)))
}
return f[i][m]
}
return dfs(0, 1)
}
function stoneGameII(piles: number[]): number {
const n = piles.length;
const f = Array.from({ length: n }, _ => new Array(n + 1).fill(0));
const s = new Array(n + 1).fill(0);
for (let i = 0; i < n; ++i) {
s[i + 1] = s[i] + piles[i];
}
const dfs = (i: number, m: number) => {
if (m * 2 >= n - i) {
return s[n] - s[i];
}
if (f[i][m]) {
return f[i][m];
}
let res = 0;
for (let x = 1; x <= m * 2; ++x) {
res = Math.max(res, s[n] - s[i] - dfs(i + x, Math.max(m, x)));
}
return (f[i][m] = res);
};
return dfs(0, 1);
}
class Solution:
def stoneGameII(self, piles: List[int]) -> int:
@cache
def dfs(i: int, m: int = 1) -> int:
if i >= len(piles):
return 0
t = inf
for x in range(1, m << 1 | 1):
t = min(t, dfs(i + x, max(m, x)))
return s[-1] - s[i] - t
s = list(accumulate(piles, initial=0))
return dfs(0)