目录
解题思路
本题的核心在于找到一个分割点,使得Alice在此分割点之前完成关卡所获得的分数严格大于Bob在此分割点之后(包括此分割点)完成关卡所获得的分数。由于Alice和Bob都采取最优策略,我们不需要模拟他们每一步的选择,而是可以通过计算前缀和来快速获取任意区间内关卡的分数总和。
解题过程
-
首先,我们创建一个前缀和数组
prefixSum
,其中prefixSum[i]
表示possible
数组中前i
个元素的分数总和(简单模式关卡得1分,困难模式关卡扣1分)。 -
接着,我们遍历
prefixSum
数组(从第二个元素开始,因为第一个元素代表只有一个关卡时的分数,不满足题目要求),对于每个分割点i
,我们计算Alice在此点之前的得分(即prefixSum[i]
)和Bob在此点之后(包括此点)的得分(理论上应该是prefixSum[n] - prefixSum[i]
,但考虑到Bob至少需要完成一个关卡,且我们希望Alice得分严格大于Bob,所以实际比较时可能不需要显式计算Bob的得分,而是比较Alice的得分与剩余关卡中可能获得的最大分数)。 -
如果在某个分割点,Alice的得分严格大于Bob在此之后可能获得的最大分数(这通常意味着Alice的得分加上至少一个关卡的最低分数仍然大于剩余关卡的分数总和),我们就找到了答案,即Alice需要完成的最少关卡数。
-
如果遍历完所有可能的分割点都没有找到满足条件的答案,则返回-1,表示Alice无法获得比Bob更高的分数。
然而,需要注意的是,上述过程中的第3步在实际编码时可能并不需要显式计算Bob的得分,因为我们可以直接通过比较Alice的得分和剩余关卡中简单模式与困难模式的数量差(或前缀和数组中的某个值)来做出判断。
时间复杂度
- 我们需要遍历一次
possible
数组来计算前缀和数组prefixSum
,这需要O(n)的时间。 - 接着,我们需要遍历一次
prefixSum
数组来找到满足条件的分割点,这同样需要O(n)的时间。 - 因此,总的时间复杂度是O(n)。
空间复杂度
- 我们创建了一个前缀和数组
prefixSum
,其长度为n+1
(为了包含整个possible
数组的前缀和以及一个初始值为0的元素),因此空间复杂度是O(n)。
Code
class Solution {
public int minimumLevels(int[] possible) {
int n = possible.length;
int[] prefixSum = new int[n + 1]; // 前缀和数组,用于快速计算任意区间的分数和
// 计算前缀和
for (int i = 0; i < n; i++) {
prefixSum[i + 1] = prefixSum[i] + (possible[i] == 0 ? -1 : 1);
}
int aliceScore = 0; // Alice的分数
int bobScore = 0; // Bob的分数
int aliceLevels = 0; // Alice完成的关卡数
// 遍历所有可能的分割点,尝试找到Alice获得更高分数的最少关卡数
for (int i = 1; i < n; i++) {
aliceScore = prefixSum[i]; // Alice完成前i个关卡所得的分数
bobScore = prefixSum[n] - aliceScore; // Bob完成剩余关卡所得的分数
// 检查Alice是否获得更高的分数
if (aliceScore > bobScore) {
// 找到第一个满足条件的分割点,即为最少关卡数
return i;
}
// 如果Alice和Bob分数相同,则Alice需要继续完成更多关卡
aliceLevels++;
}
// 如果没有找到满足条件的分割点,则返回-1
return -1;
}
}