LeetCode 1403 - Minimum Subsequence in Non-Increasing Order
The problem gives an integer array nums and asks us to build a subsequence whose sum is strictly greater than the sum of
Difficulty: 🟢 Easy
Topics: Array, Greedy, Sorting
Solution
Problem Understanding
The problem gives an integer array nums and asks us to build a subsequence whose sum is strictly greater than the sum of all elements that are not included in the subsequence.
A subsequence means we are allowed to remove elements from the array while preserving the remaining elements. However, the final answer must be returned in non-increasing order, so we are effectively free to sort the selected values before returning them.
The problem introduces two optimization requirements:
- The subsequence must have the minimum possible size.
- If multiple subsequences have the same minimum size, we must choose the one with the maximum total sum.
The input size is relatively small:
1 <= nums.length <= 5001 <= nums[i] <= 100
These constraints tell us several important things:
- The array is small enough that sorting is completely acceptable.
- Values are positive integers only, which is extremely important.
- Because all numbers are positive, taking larger numbers first always increases the subsequence sum as quickly as possible.
- The problem guarantees that the final answer is unique.
The core condition can be rewritten mathematically.
If:
selected_sumis the sum of the chosen subsequencetotal_sumis the sum of the entire array
then the requirement becomes:
selected_sum > total_sum - selected_sum
which simplifies to:
2 * selected_sum > total_sum
So our goal is to pick as few numbers as possible so that their sum exceeds half of the total array sum.
Several edge cases are important:
- Arrays with only one element, where that element alone automatically satisfies the condition.
- Arrays where multiple large elements are equal.
- Arrays where the subsequence sum becomes exactly equal to the remaining sum, which is not sufficient because the condition is strictly greater.
- Arrays already sorted in increasing or decreasing order.
- Arrays where many small numbers require selecting several elements before crossing half the total sum.
Approaches
Brute Force Approach
A brute force solution would generate every possible subsequence of the array and test whether it satisfies the required condition.
For each subsequence:
- Compute the sum of selected elements.
- Compute the sum of unselected elements.
- Check whether the selected sum is strictly greater.
- Among all valid subsequences, choose:
- the one with minimum size
- then the one with maximum sum if sizes tie
This approach is correct because it exhaustively examines every possible subsequence, guaranteeing that the optimal answer will eventually be found.
However, the number of subsequences of an array with n elements is:
2^n
With n = 500, this becomes astronomically large and completely infeasible.
Optimal Greedy Approach
The key observation is that all numbers are positive.
If we want the subsequence to exceed half of the total sum while using the fewest elements possible, we should always choose the largest available numbers first.
Why does this work?
- Larger numbers increase the subsequence sum faster.
- Using larger values minimizes how many elements we need.
- If two subsequences have the same size, choosing larger numbers naturally produces the larger total sum.
This leads to a simple greedy strategy:
- Sort the array in descending order.
- Keep taking numbers from largest to smallest.
- Stop as soon as the selected sum becomes strictly greater than the remaining sum.
Because the numbers are processed from largest to smallest, the resulting subsequence is automatically in non-increasing order.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^n * n) | O(n) | Generates and checks all subsequences |
| Optimal | O(n log n) | O(n) | Sort descending and greedily select largest values |
Algorithm Walkthrough
- Compute the total sum of the array.
We need this to compare the subsequence sum against the remaining elements. Since the remaining sum equals total_sum - selected_sum, knowing the total allows constant-time comparisons.
2. Sort the array in descending order.
The greedy insight is that larger numbers help us reach the target condition faster. Sorting descending ensures we always consider the largest available element first. 3. Initialize:
current_sum = 0- an empty result list
current_sum tracks the sum of the chosen subsequence.
4. Iterate through the sorted array.
For each number:
- add it to the result
- add its value to
current_sum
- After each addition, check whether:
current_sum > total_sum - current_sum
If true, we have found a valid subsequence. 6. Immediately return the result.
Because we processed numbers from largest to smallest:
- we used the fewest possible elements
- among equal-sized solutions, we achieved the maximum possible sum
Why it works
The greedy strategy is optimal because every number is positive. Choosing a larger number always contributes more toward exceeding half the total sum than choosing a smaller number.
Suppose there were a better solution using fewer elements than the greedy approach. That would mean smaller numbers somehow achieved the target faster than larger numbers, which is impossible with strictly positive values.
Additionally, if two solutions use the same number of elements, taking the largest available numbers guarantees the maximum subsequence sum.
Therefore, sorting descending and greedily selecting elements produces the unique optimal answer.
Python Solution
from typing import List
class Solution:
def minSubsequence(self, nums: List[int]) -> List[int]:
total_sum = sum(nums)
nums.sort(reverse=True)
result = []
current_sum = 0
for num in nums:
result.append(num)
current_sum += num
if current_sum > total_sum - current_sum:
return result
return result
The implementation begins by computing the total array sum. This allows us to determine the remaining sum at any moment without repeatedly recalculating it.
Next, the array is sorted in descending order. This is the core greedy step because we want to take the largest numbers first.
The result list stores the chosen subsequence, while current_sum tracks the sum of selected elements.
During iteration, each number is added to both the subsequence and the running sum. After every addition, we check whether the selected sum is strictly greater than the remaining sum.
The moment the condition becomes true, we immediately return the result because:
- we have used the fewest elements possible
- the chosen elements are the largest available ones
- the subsequence is already in non-increasing order
The final return result is technically unnecessary under the problem guarantees, but including it makes the function complete and safe.
Go Solution
package main
import "sort"
func minSubsequence(nums []int) []int {
totalSum := 0
for _, num := range nums {
totalSum += num
}
sort.Slice(nums, func(i, j int) bool {
return nums[i] > nums[j]
})
result := []int{}
currentSum := 0
for _, num := range nums {
result = append(result, num)
currentSum += num
if currentSum > totalSum-currentSum {
return result
}
}
return result
}
The Go implementation follows exactly the same greedy strategy as the Python version.
The main difference is sorting syntax. Go uses sort.Slice with a custom comparator function to sort the array in descending order.
The result subsequence is stored in a slice. As numbers are appended, the running sum is updated and checked against the remaining sum.
Integer overflow is not a concern because the constraints are very small. The maximum possible total sum is:
500 * 100 = 50000
which easily fits inside Go's int type.
Worked Examples
Example 1
Input:
nums = [4,3,10,9,8]
First compute the total sum:
total_sum = 34
Sort descending:
[10,9,8,4,3]
Now iterate greedily.
| Step | Picked Number | Result | Current Sum | Remaining Sum | Condition |
|---|---|---|---|---|---|
| 1 | 10 | [10] | 10 | 24 | 10 > 24, false |
| 2 | 9 | [10,9] | 19 | 15 | 19 > 15, true |
We stop immediately.
Final answer:
[10,9]
Example 2
Input:
nums = [4,4,7,6,7]
Compute total sum:
total_sum = 28
Sort descending:
[7,7,6,4,4]
Iterate greedily.
| Step | Picked Number | Result | Current Sum | Remaining Sum | Condition |
|---|---|---|---|---|---|
| 1 | 7 | [7] | 7 | 21 | 7 > 21, false |
| 2 | 7 | [7,7] | 14 | 14 | 14 > 14, false |
| 3 | 6 | [7,7,6] | 20 | 8 | 20 > 8, true |
We stop after selecting three numbers.
Final answer:
[7,7,6]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting dominates the runtime |
| Space | O(n) | Result list may store all elements |
The sorting operation is the most expensive part of the algorithm, requiring O(n log n) time.
The iteration afterward is linear, so it does not change the overall complexity.
The extra space comes primarily from the output subsequence. In the worst case, all elements may need to be included.
Test Cases
from typing import List
class Solution:
def minSubsequence(self, nums: List[int]) -> List[int]:
total_sum = sum(nums)
nums.sort(reverse=True)
result = []
current_sum = 0
for num in nums:
result.append(num)
current_sum += num
if current_sum > total_sum - current_sum:
return result
return result
solution = Solution()
assert solution.minSubsequence([4, 3, 10, 9, 8]) == [10, 9] # Example 1
assert solution.minSubsequence([4, 4, 7, 6, 7]) == [7, 7, 6] # Example 2
assert solution.minSubsequence([1]) == [1] # Single element array
assert solution.minSubsequence([2, 1]) == [2] # Largest element alone works
assert solution.minSubsequence([1, 1, 1, 1]) == [1, 1, 1] # Need more than half
assert solution.minSubsequence([5, 5, 5, 5]) == [5, 5, 5] # Equality is not enough
assert solution.minSubsequence([10, 1, 1, 1]) == [10] # One dominant element
assert solution.minSubsequence([6, 6, 6, 6, 6]) == [6, 6, 6] # Multiple equal values
assert solution.minSubsequence([9, 8, 7, 6]) == [9, 8] # Already descending
assert solution.minSubsequence([1, 2, 3, 4, 5]) == [5, 4] # Increasing order input
assert solution.minSubsequence([100] * 500) == [100] * 251 # Maximum size stress test
| Test | Why |
|---|---|
[4,3,10,9,8] |
Validates the first provided example |
[4,4,7,6,7] |
Validates strict inequality handling |
[1] |
Smallest possible input |
[2,1] |
Largest element alone satisfies condition |
[1,1,1,1] |
Requires selecting multiple small values |
[5,5,5,5] |
Ensures equality does not count |
[10,1,1,1] |
Tests dominant large value |
[6,6,6,6,6] |
Handles repeated equal values |
[9,8,7,6] |
Input already sorted descending |
[1,2,3,4,5] |
Input sorted ascending |
[100] * 500 |
Stress test with maximum constraints |
Edge Cases
One important edge case occurs when the array contains only a single element. Since all numbers are positive, that single element is automatically greater than the sum of the remaining elements, which is zero. The implementation handles this naturally because the first iteration immediately satisfies the condition.
Another subtle case is when the selected sum becomes exactly equal to the remaining sum. The problem requires the selected sum to be strictly greater, not greater than or equal. For example, in [4,4,7,6,7], selecting [7,7] produces sums of 14 and 14, which is invalid. The implementation correctly uses the > operator instead of >=.
A third important case involves many identical values. For arrays like [5,5,5,5], selecting two elements gives 10 versus 10, which still fails the condition. The algorithm continues selecting values until the inequality becomes strictly true, ensuring correctness even when many values are equal.
Another edge case appears when one value dominates the entire array, such as [10,1,1,1]. In this situation, the optimal subsequence contains only one element. Because the algorithm processes numbers in descending order, it immediately identifies this minimal solution.
Finally, arrays already sorted in ascending or descending order should still work correctly. Since the algorithm explicitly sorts the array before processing, the original ordering does not affect correctness.