leetcode
leetcode 1501 ~ 1550
石子游戏 VII

石子游戏 VII

难度:

标签:

题目描述

代码结果

运行时间: 610 ms, 内存: 16.0 MB


/*
题目思路:
1. 使用动态规划来解决这个问题。
2. dp[i][j] 表示从 i 到 j 的石子数组中,当前玩家与另一个玩家得分差的最大值。
3. 如果当前玩家选择左边的石子,得分差为 stones[i] - dp[i+1][j]。
4. 如果当前玩家选择右边的石子,得分差为 stones[j] - dp[i][j-1]。
5. 转移方程为 dp[i][j] = max(stones[i] - dp[i+1][j], stones[j] - dp[i][j-1])。
6. 最终结果为 dp[0][n-1]。
*/
import java.util.stream.IntStream;
public int stoneGameVII(int[] stones) {
    int n = stones.length;
    int[][] dp = new int[n][n];
    int[] prefixSum = IntStream.range(0, n + 1).map(i -> i == 0 ? 0 : IntStream.range(0, i).map(j -> stones[j]).sum()).toArray();
    IntStream.range(1, n).forEach(length ->
        IntStream.range(0, n - length).forEach(i -> {
            int j = i + length;
            int sum = prefixSum[j + 1] - prefixSum[i];
            dp[i][j] = Math.max(sum - stones[i] - dp[i + 1][j], sum - stones[j] - dp[i][j - 1]);
        })
    );
    return dp[0][n - 1];
}

解释

方法:

该题解采用了动态规划的方法。dp[i] 表示从石头 i 到当前考虑的石头 j 的区间内,当前玩家与另一玩家的得分差的最大值。在每一轮中,玩家可以选择移除最左边或最右边的石头,然后更新 dp[i] 为剩余石头的总和减去 dp[i+1](如果移除左边石头)和 dp[i](如果移除右边石头)中的较大值,以保持得分差最大化。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中,动态规划数组`dp`的初始值为`stones`数组本身,这种初始化方法的合理性何在?
题解中将动态规划数组`dp`的初始值设置为`stones`数组本身,这是因为在只有一个石头的情况下,如果一个玩家选择了它,那么这个玩家的得分差相对于对手就是0(因为没有剩余的石头可以给对手得分)。因此,当考虑只有一个石子的情况时,得分差是0,这也是`dp`数组初始化为`stones`的理由。在后续的迭代中,这个初始值将被更新以表示更大区间的最优得分差。
🦆
题解中的动态规划状态转移方程`dp[i] = acc - (dp[i + 1] if dp[i + 1] > dp[i] else dp[i])`是如何确保当前玩家与对手的得分差最大化的?
状态转移方程`dp[i] = acc - (dp[i + 1] if dp[i + 1] > dp[i] else dp[i])`用于计算从石头i到j的区间内,当前玩家与对手的最大得分差。这里,`acc`是从石头i到j的石头总和,`dp[i+1]`和`dp[i]`分别代表移除左边或右边石头后的得分差。当前玩家会选择一个操作使得对手在下一步的得分差最小化,从而确保自己的得分差最大化。这种策略是一种典型的min-max策略,用于在对抗性游戏中确保一方的最优结果。
🦆
题解示例实现中,最后返回的是`acc - dp[0]`,其中`acc`表示什么?为何最终的得分差是用这两者的差值来表示?
`acc`在代码中表示的是整个`stones`数组的总和,即所有石头的总和。最后返回`acc - dp[0]`是因为`dp[0]`在最终的迭代中表示的是从第一个石头到最后一个石头的最大得分差,而`acc`表示所有石头的总分。因此,`acc - dp[0]`表示在两个玩家都采取最优策略的情况下,最先操作的玩家可以达到的最大得分差。
🦆
在动态规划解法中,为何要倒序更新`i`从`j-1`到`0`?直接正序更新有何不妥?
在动态规划解法中,倒序更新`i`从`j-1`到`0`是为了确保在计算`dp[i]`时,所需要的`dp[i+1]`已经被正确计算和更新过。如果我们正序更新`i`,则在计算`dp[i]`时,`dp[i+1]`可能尚未更新,导致依赖的数据不正确。倒序更新保证了在更新某个`dp[i]`的值之前,其依赖的所有后续值已经是最新的,从而保证了动态规划状态的正确转移。

相关问题