leetcode
leetcode 1651 ~ 1700
石子游戏 VIII

石子游戏 VIII

难度:

标签:

题目描述

代码结果

运行时间: 131 ms, 内存: 27.4 MB


/**
 * 思路:
 * 使用动态规划和 Java Stream API 来解决问题。
 * 我们依然使用前缀和数组 prefixSums 来计算区间和。
 * 通过 streams 来遍历和计算 dp 数组。
 */
import java.util.stream.IntStream;

public class StoneGameStream { 
    public int stoneGameVII(int[] stones) { 
        int n = stones.length; 
        int[] prefixSums = IntStream.rangeClosed(1, n) 
                                     .map(i -> IntStream.of(stones).limit(i).sum()) 
                                     .toArray(); 
        int[][] dp = new int[n][n]; 
        IntStream.range(2, n + 1).forEach(length -> 
            IntStream.range(0, n - length + 1).forEach(i -> { 
                int j = i + length - 1; 
                dp[i][j] = Math.max(prefixSums[j + 1] - prefixSums[i + 1] - dp[i + 1][j], 
                                     prefixSums[j] - prefixSums[i] - dp[i][j - 1]); 
            }) 
        ); 
        return dp[0][n - 1]; 
    } 
}

解释

方法:

这道题解的思路是动态规划。首先,通过累加前缀和,将stones数组转换为存储从第一个石头到当前位置所有石头的总和。接着,从数组末尾向前遍历,计算每个位置可以给后手带来的最小分差。具体来说,对于每个位置j,计算当Alice到达该位置时,如果接下来由Bob开始,他们分数差的最优解。这是通过取当前位置的前缀和减去之前位置得到的最大收益来计算的。通过这种方式,递推得到最终的结果。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到的前缀和数组是如何帮助理解和解决这个问题的?
前缀和数组在这个问题中的作用主要是为了快速计算从数组的任意起始位置到任意结束位置的石头总和。通过前缀和,我们可以在常数时间内得到任意区间的石子总数,这是动态规划状态转移的基础。在这道题中,前缀和数组的使用使得我们能够快速计算出在某个位置j选择停止后,Alice与Bob之间的得分差。这种快速计算区间和的能力极大地简化了问题的复杂性,使得我们能够专注于动态规划的状态转移逻辑。
🦆
动态规划的转移方程是怎样的,可以详细解释一下吗?
动态规划的核心在于维护一个变量 `curmax`,这个变量表示Alice可以确保的最大分差。对于每个位置j,我们计算 `profit = stones[j] - curmax`。这里,`stones[j]` 是Alice在位置j停止后可以获得的总分数,而`curmax`是到目前为止Bob可以通过最优选择获得的最大分数的负值。如果`profit`大于`curmax`,则更新`curmax`为`profit`。这样的转移方程确保了每一步都考虑了使Alice分数最大化的可能性,从而找到最终的最优解。
🦆
为什么动态规划的过程中需要从数组的末尾向前遍历?
从数组末尾向前遍历的原因是这样可以更自然地模拟游戏的决策过程。在这个问题中,Alice和Bob在做出决策时,总是考虑当前位置和之前的最优决策结果。从后向前遍历可以让我们在确定当前位置的最优决策时,已经知道所有后续位置的最优结果,这符合动态规划“由后向前”的特性。此外,这种遍历方式也是因为题目的游戏规则导致的,即Alice选择停止的位置越往前,她能够确保的优势越大,因此我们需要从后往前评估每个位置的价值。
🦆
在题解中提到,更新当前最大分差 `curmax` 时使用的条件是 `profit > curmax`,这样的更新逻辑是如何确保最终结果是最优的?
这样的更新逻辑确保了在每个位置j,如果选择停止可以获得比之前任何位置更大的分差,则更新`curmax`。这意味着`curmax`总是保持为Alice可以通过最优策略达到的最大分差。这种方法是贪心策略的一种体现——在每一步都尝试取得当前可获得的最大利益。通过这种方式,我们可以确保在遍历结束时,`curmax`存储的是Alice在所有可能的停止位置中,能够确保的最大分差,从而达到全局最优。

相关问题