Maximum Strength of a Group
MedPrompt
You are given a 0-indexed integer array nums representing the score of students in an exam. The teacher would like to form one non-empty group of students with maximal strength, where the strength of a group of students of indices i0, i1, i2, ... , ik is defined as nums[i0] * nums[i1] * nums[i2] * ... * nums[ik].
Return the maximum strength of a group the teacher can create.
Example 1:
Input: nums = [3,-1,-5,2,5,-9]
Output: 1350
Explanation: One way to form a group of maximal strength is to group the students at indices [0,2,3,4,5]. Their strength is 3 * (-5) * 2 * 5 * (-9) = 1350, which we can show is optimal.Example 2:
Input: nums = [-4,-5,-4]
Output: 20
Explanation: Group the students at indices [0, 1] . Then, we’ll have a resulting strength of 20. We cannot achieve greater strength.
Constraints:
1 <= nums.length <= 13-9 <= nums[i] <= 9
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves generating all possible non-empty subsets of the given nums array and calculating the product (strength) for each. The maximum product found among all subsets is the answer. Given the small constraint on the input size (nums.length <= 13), exploring all 2^N - 1 non-empty subsets is computationally feasible. We can implement this using a backtracking algorithm.
Algorithm
- Initialize a global variable
maxStrengthto a very small number (e.g.,Long.MIN_VALUE). - Create a recursive function
backtrack(index, currentProduct, count). Thecountparameter tracks the number of elements in the current subset to handle the non-empty constraint. - Base Case: If
indexreaches the end of thenumsarray:- If
count > 0, it means the current subset is non-empty. Update the global maximum:maxStrength = Math.max(maxStrength, currentProduct). - Return from the function.
- If
- Recursive Step: For the element at the current
index, explore two possibilities:- Include
nums[index]: Make a recursive callbacktrack(index + 1, currentProduct * nums[index], count + 1). - Exclude
nums[index]: Make another recursive callbacktrack(index + 1, currentProduct, count).
- Include
- To start the process, make an initial call
backtrack(0, 1L, 0)from the main function. - After the recursion completes,
maxStrengthwill hold the maximum product of any non-empty subset.
Walkthrough
A backtracking function is used to explore all subset combinations. The function, say backtrack(index, currentProduct, count), keeps track of the current index in the array, the product of the elements chosen so far, and the number of elements in the current subset.
For each element, we branch into two recursive calls: one where the element is included in the subset (and its value is multiplied into currentProduct), and one where it's excluded. When we reach the end of the array, we have formed a complete subset. If this subset is not empty (which we check using the count variable), we compare its product with a globally maintained maximum strength and update it if the current product is greater.
The initial call starts at index 0 with a product of 1 and a count of 0. The use of a long for the product is crucial to prevent overflow, as the strength can become very large.
class Solution { long maxStrength = Long.MIN_VALUE; public long maxStrength(int[] nums) { backtrack(0, 1L, 0, nums); return maxStrength; } private void backtrack(int index, long currentProduct, int count, int[] nums) { if (index == nums.length) { if (count > 0) { maxStrength = Math.max(maxStrength, currentProduct); } return; } // Choice 1: Include nums[index] backtrack(index + 1, currentProduct * nums[index], count + 1, nums); // Choice 2: Exclude nums[index] backtrack(index + 1, currentProduct, count, nums); }}Complexity
Time
O(2^N), where N is the length of `nums`. For each element, there are two choices (include or exclude), leading to `2^N` total subsets to explore.
Space
O(N), where N is the length of `nums`. This space is used by the recursion stack, which can go up to N levels deep.
Trade-offs
Pros
Conceptually simple and straightforward to implement.
Guaranteed to find the correct answer as it exhaustively checks every possibility.
Cons
Highly inefficient for larger input sizes.
The time complexity grows exponentially with the size of the input array.
It is only feasible because of the small problem constraint (nums.length <= 13).
A greedy approach provides a highly efficient solution by making locally optimal choices. The key insight is how different types of numbers affect the product. Positive numbers always increase the strength, so we should include all of them. Negative numbers are beneficial in pairs. An even number of negative numbers results in a positive product, while an odd number results in a negative one. If we must have a negative product, we want it to be as close to zero as possible. If we have an odd number of negatives, we can make the product positive by excluding one of them. To maximize the result, we should exclude the negative number with the smallest absolute value (the one closest to zero).
Algorithm
- Handle the base case: If the array has only one element, its strength is the element itself.
- Initialize variables:
product = 1L,negCount = 0,posCount = 0,zeroCount = 0, andmaxNeg = Integer.MIN_VALUEto track the largest negative number (closest to zero). - Iterate through the
numsarray once to populate these variables.- For a positive number, multiply it into
productand incrementposCount. - For a negative number, multiply it into
product, incrementnegCount, and updatemaxNeg. - For a zero, just increment
zeroCount.
- For a positive number, multiply it into
- After the loop, analyze the counts to determine the final result:
- If the array contained only zeros, the result is 0.
- If the count of negative numbers (
negCount) is odd:- A special case: if there are no positive numbers (
posCount == 0), only one negative number (negCount == 1), and at least one zero (zeroCount > 0), the maximum strength is 0 (by choosing a zero). - Otherwise, to maximize the product, we must remove the effect of one negative number. We remove the one with the smallest absolute value, which is
maxNeg. This is done by dividingproductbymaxNeg.
- A special case: if there are no positive numbers (
- If
negCountis even, the product of all non-zero numbers is already positive and maximized. - Return the final calculated
product.
Walkthrough
This approach avoids generating subsets and instead calculates the result based on properties of the numbers in the array. We iterate through the array just once to gather key statistics.
We maintain a running product of all non-zero numbers. We also count the number of positive, negative, and zero elements. Crucially, we keep track of the negative number with the highest value (e.g., -1 is greater than -5).
After the initial pass, we check the count of negative numbers. If it's even, the total product of non-zero numbers is already the maximum possible strength. If the count is odd, the product is negative. To make it positive and maximize it, we must effectively remove one negative number. The best one to remove is the one that has the least impact on the magnitude, which is the negative number with the smallest absolute value (maxNeg). We achieve this by dividing our running product by maxNeg.
Edge cases are critical: if the array contains only zeros, the answer is 0. If it contains only one negative number and some zeros, the max strength is 0 (by picking a zero instead of the negative number). The code handles these scenarios to ensure correctness.
class Solution { public long maxStrength(int[] nums) { if (nums.length == 1) { return nums[0]; } long product = 1; int negCount = 0; int posCount = 0; int zeroCount = 0; int maxNeg = Integer.MIN_VALUE; boolean hasNonZero = false; for (int num : nums) { if (num > 0) { product *= num; posCount++; hasNonZero = true; } else if (num < 0) { product *= num; negCount++; maxNeg = Math.max(maxNeg, num); hasNonZero = true; } else { zeroCount++; } } if (!hasNonZero) { // All zeros return 0; } if (negCount % 2 != 0) { // Odd number of negatives // If there's only one negative and no positives, the max strength is 0 (if zeros exist) if (negCount == 1 && posCount == 0 && zeroCount > 0) { return 0; } // To maximize, remove the negative number with the smallest absolute value product /= maxNeg; } return product; }}Complexity
Time
O(N), where N is the length of `nums`. The algorithm involves a single pass through the array.
Space
O(1). We only use a fixed number of variables to store counts and the running product, regardless of the input size.
Trade-offs
Pros
Extremely efficient with linear time complexity.
Uses constant extra space, making it optimal for memory usage.
Scales well to much larger input sizes beyond the problem's constraints.
Cons
The logic can be complex due to the need to handle multiple edge cases involving zeros, single negative numbers, and combinations thereof.
Solutions
Solution
class Solution {public long maxStrength(int[] nums) { Arrays.sort(nums); int n = nums.length; if (n == 1) { return nums[0]; } if (nums[1] == 0 && nums[n - 1] == 0) { return 0; } long ans = 1; int i = 0; while (i < n) { if (nums[i] < 0 && i + 1 < n && nums[i + 1] < 0) { ans *= nums[i] * nums[i + 1]; i += 2; } else if (nums[i] <= 0) { i += 1; } else { ans *= nums[i]; i += 1; } } return ans; }}class Solution {public: long long maxStrength(vector<int> &nums) { sort(nums.begin(), nums.end()); int n = nums.size(); if (n == 1) { return nums[0]; } if (nums[1] == 0 && nums[n - 1] == 0) { return 0; } long long ans = 1; int i = 0; while (i < n) { if (nums[i] < 0 && i + 1 < n && nums[i + 1] < 0) { ans *= nums[i] * nums[i + 1]; i += 2; } else if (nums[i] <= 0) { i += 1; } else { ans *= nums[i]; i += 1; } } return ans; }};class Solution: def maxStrength(self, nums: List[int]) -> int: nums . sort() n = len(nums) if n == 1: return nums[0] if nums[1] == nums[- 1] == 0: return 0 ans, i = 1, 0 while i < n: if nums[i] < 0 and i + 1 < n and nums[i + 1] < 0: ans *= nums[i] * nums[i + 1] i += 2 elif nums[i] <= 0: i += 1 else: ans *= nums[i] i += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.