【每日一题】LeetCode 2708.最大实力值小组(一次遍历、分类讨论、动态规划)
题目描述
给定一个整数数组 nums
,表示一个班级中所有学生在一次考试中的成绩。老师想从这个班级中选出一部分同学组成一个非空小组,使得这个小组的实力值最大。小组的实力值定义为小组中所有学生成绩的乘积。请返回老师创建的小组能得到的最大实力值。
思路分析
这个问题可以通过动态规划的思想来解决。我们需要维护两个变量,mn
和 mx
,分别表示当前遍历到的元素之前可能的最小乘积和最大乘积。对于数组中的每个元素,我们考虑两种情况:不包括当前元素和包括当前元素。对于每种情况,我们分别更新 mn
和 mx
。
- 不包括当前元素:
mn
和mx
保持不变。 - 包括当前元素:我们需要考虑当前元素与之前最小乘积和最大乘积相乘的情况。
由于数组中可能包含负数,我们需要特别注意负数的乘积可能会变成正数,这可能会影响最终的最大乘积。
输入示例
输入: nums = [3, -1, -5, 2, 5, -9]
输出: 1350
推导验证
初始化
mn = 3
(数组的第一个元素)mx = 3
(同上)
遍历数组并更新 mn
和 mx
遍历到 -1
x = -1
tmpe = mn = 3
(保存当前mn
的值,用于更新mx
)
更新 mn
:
- 不选
-1
,mn
不变,仍然是3
- 选
-1
,新的mn
为-1
mn * x = 3 * (-1) = -3
mx * x = 3 * (-1) = -3
- 新的
mn
为Math.min(3, -1, -3, -3) = -3
更新 mx
:
- 不选
-1
,mx
不变,仍然是3
- 选
-1
,新的mx
为-1
tmpe * x = 3 * (-1) = -3
mx * x = 3 * (-1) = -3
- 新的
mx
为Math.max(3, -1, -3, -3) = 3
遍历到 -5
x = -5
tmpe = mn = -3
(保存当前mn
的值,用于更新mx
)
更新 mn
:
- 不选
-5
,mn
不变,仍然是-3
- 选
-5
,新的mn
为-5
mn * x = -3 * (-5) = 15
mx * x = 3 * (-5) = -15
- 新的
mn
为Math.min(-3, -5, 15, -15) = -5
更新 mx
:
- 不选
-5
,mx
不变,仍然是3
- 选
-5
,新的mx
为-5
tmpe * x = -3 * (-5) = 15
mx * x = 3 * (-5) = -15
- 新的
mx
为Math.max(3, -5, 15, -15) = 15
遍历到 2
x = 2
tmpe = mn = -5
(保存当前mn
的值,用于更新mx
)
更新 mn
:
- 不选
2
,mn
不变,仍然是-5
- 选
2
,新的mn
为2
mn * x = -5 * 2 = -10
mx * x = 15 * 2 = 30
- 新的
mn
为Math.min(-5, 2, -10, 30) = -5
更新 mx
:
- 不选
2
,mx
不变,仍然是15
- 选
2
,新的mx
为2
tmpe * x = -5 * 2 = -10
mx * x = 15 * 2 = 30
- 新的
mx
为Math.max(15, 2, -10, 30) = 30
遍历到 5
x = 5
tmpe = mn = -5
(保存当前mn
的值,用于更新mx
)
更新 mn
:
- 不选
5
,mn
不变,仍然是-5
- 选
5
,新的mn
为5
mn * x = -5 * 5 = -25
mx * x = 30 * 5 = 150
- 新的
mn
为Math.min(-5, 5, -25, 150) = -5
更新 mx
:
- 不选
5
,mx
不变,仍然是30
- 选
5
,新的mx
为5
tmpe * x = -5 * 5 = -25
mx * x = 30 * 5 = 150
- 新的
mx
为Math.max(30, 5, -25, 150) = 150
遍历到 -9
x = -9
tmpe = mn = -5
(保存当前mn
的值,用于更新mx
)
更新 mn
:
- 不选
-9
,mn
不变,仍然是-5
- 选
-9
,新的mn
为-9
mn * x = -5 * (-9) = 45
mx * x = 150 * (-9) = -1350
- 新的
mn
为Math.min(-5, -9, 45, -1350) = -9
更新 mx
:
- 不选
-9
,mx
不变,仍然是150
- 选
-9
,新的mx
为-9
tmpe * x = -5 * (-9) = 45
mx * x = 150 * (-9) = -1350
- 新的
mx
为Math.max(150, -9, 45, -1350) = 1350
结果
最终,mx
的值为 1350
,这是所有可能子集中的最大乘积。
通过这个详细的步骤,我们可以看到每次迭代是如何考虑当前元素 x
是否加入到子集中,以及如何影响最小乘积 mn
和最大乘积 mx
的。这种方法确保了我们可以找到最大的乘积,而不需要枚举所有可能的子集。
代码实现
class Solution {
public long maxStrength(int[] nums) {
long mn = nums[0]; // 初始化最小乘积为数组的第一个元素
long mx = mn; // 初始化最大乘积也为数组的第一个元素
for (int i = 1; i < nums.length; i++) {
long x = nums[i]; // 当前遍历到的元素
long oldMn = mn; // 保存当前的最小乘积,以便在更新时使用
long oldMx = mx; // 保存当前的最大乘积,以便在更新时使用
// 更新最小乘积
// 考虑不包括当前元素和包括当前元素的情况
mn = Math.min(Math.min(mn, x), Math.min(oldMn * x, oldMx * x));
// 更新最大乘积
// 考虑不包括当前元素和包括当前元素的情况
mx = Math.max(Math.max(mx, x), Math.max(oldMn * x, oldMx * x));
}
return mx; // 返回最大乘积
}
}