石子游戏 II
难度:
标签:
题目描述
代码结果
运行时间: 93 ms, 内存: 16.4 MB
/*
* 思路:使用动态规划来解决这个问题。dp[i][M]表示当剩下的石子堆为piles[i:]时,M的值为M时,当前玩家最多可以拿到的石子数量。
* 我们从后向前计算,最终dp[0][1]就是我们要的答案。
*/
import java.util.stream.IntStream;
public class Solution {
public int stoneGameII(int[] piles) {
int n = piles.length;
int[][] dp = new int[n][n + 1];
int[] suffixSum = new int[n];
suffixSum[n - 1] = piles[n - 1];
IntStream.range(0, n - 1).forEach(i -> suffixSum[n - 2 - i] = suffixSum[n - 1 - i] + piles[n - 2 - i]);
IntStream.range(0, n).forEach(i -> {
IntStream.range(1, n + 1).forEach(M -> {
if (i + 2 * M >= n) {
dp[n - 1 - i][M] = suffixSum[n - 1 - i];
} else {
IntStream.range(1, 2 * M + 1).forEach(X -> {
dp[n - 1 - i][M] = Math.max(dp[n - 1 - i][M], suffixSum[n - 1 - i] - dp[n - 1 - i + X][Math.max(M, X)]);
});
}
});
});
return dp[0][1];
}
}
解释
方法:
这个问题可以通过动态规划来解决。定义 dp[i][m] 为从第 i 堆开始,M 值为 m 时,当前玩家可以获得的最大石子数。s 用来记录从当前堆到最后一堆的石子总数(后缀和)。如果从第 i 堆开始,玩家可以选择拿 1 到 2m 堆的石子,那么 dp[i][m] 就应该是 s 减去后续玩家可能的最小值,即 s - min(dp[i+x][max(m,x)] for x in range(1, m*2+1))。如果 i + m * 2 超出了石子堆的数量,即 i+m*2 >= n,当前玩家可以直接拿走所有剩余的石子,dp[i][m] = s。最终解为 dp[0][1],即从第一堆开始,M=1 的情况。
时间复杂度:
O(n^2)
空间复杂度:
O(n^2)
代码细节讲解
🦆
题解中提到的动态规划数组`dp[i][m]`具体是怎样定义的?能否详细解释其代表的意义?
▷🦆
在题解的动态规划过程中,为何要计算`s - min(dp[i+x][max(m,x)] for x in range(1, m*2+1))`,这样的计算逻辑是基于什么考虑?
▷🦆
题解中更新后缀和`s`的代码是如何实现的?这个后缀和在整个动态规划中起到了什么作用?
▷🦆
动态规划解法中提到当`i + m * 2 >= n`时,当前玩家可以直接拿走所有剩余的石子,为什么会有这样的结论?
▷