Monotonic Subsequence Sum — Problem Statement & Solution Guide
Problem Description
Given an array or sequence of length $N$ representing numerical values or system metrics, compute the monotonic subsequence sum according to the target algorithm rules. The monotonic subsequence sum is the maximum sum of a monotonic subsequence in the array.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Monotonic Subsequence Sum"
WHY DOES IT MATTER?
Maximum‑sum monotonic subsequence is a canonical example of DP‑optimisation via data structures; mastering it equips engineers to turn quadratic DP into logarithmic solutions, a skill that recurs in range‑query, LIS, and knapsack‑style problems.
OPTIMIZATION CHALLENGE
The breakthrough is recognizing that the DP transition depends only on the best sum for all smaller (or equal) values, which can be answered with a prefix‑max query; a Fenwick or segment tree reduces the naïve O(N) scan per element to O(log N).
REAL-WORLD CONNECTION
Think of a financial time‑series where you want the highest cumulative profit while only buying when prices are non‑decreasing; the algorithm mirrors real‑time portfolio optimisation that respects market monotonicity constraints.
During the interview, first write the O(N^2) DP to prove correctness, then immediately discuss value compression and the prefix‑max data structure—this shows both problem‑solving depth and awareness of performance trade‑offs.
COMPLEXITY AT A GLANCE
O(N log N)O(N)Core Theory — Why This Approach?
The monotonic subsequence sum problem asks for the maximum possible sum of a subsequence whose elements are monotonic (typically non‑decreasing). A naïve solution enumerates every subset, leading to O(2^N) time, or uses a double loop DP that computes dp[i] = max sum ending at i by scanning all previous j < i with arr[j] ≤ arr[i]; this costs O(N^2) and quickly exceeds limits for N up to 10^5. The optimal paradigm treats the problem as a variant of the Longest Increasing Subsequence, but instead of length we propagate sums. By compressing the value domain and maintaining a Fenwick (Binary Indexed) Tree or Segment Tree that stores the best sum for each prefix of values, we can query the maximum sum for all values ≤ current element in O(log M) time (M = number of distinct values) and update the structure with the new sum, achieving overall O(N log N) time and O(M) space.
Interview Questions on This Problem
Q1How would you modify the classic LIS O(N log N) algorithm to compute the maximum sum of an increasing subsequence instead of its length?
Replace the binary‑search on tail values with a Fenwick/segment tree that stores the best sum for each value. For each element x, query the maximum sum for all values ≤ x, add x, and update the tree at position x with the new sum if it is larger.
Q2Explain why a simple O(N^2) DP solution may still pass for N ≤ 10^3 but fails for N = 10^5, and how you would detect this during a coding interview.
The O(N^2) DP performs ~10^10 operations at N = 10^5, which is infeasible within typical time limits. In an interview, you can point out the input constraints, estimate the operation count, and propose a more scalable O(N log N) approach using a balanced tree or BIT.
Q3In a distributed monitoring system, you need to compute the maximum sum of a monotonic trend across streaming metrics. Which data structure would you choose to maintain the answer in real time and why?
A Fenwick Tree (or segment tree) over the compressed metric values works because it supports O(log M) point updates and prefix‑max queries, allowing the system to ingest each new metric, update the best sum for its value, and instantly retrieve the global maximum.
Examples
Input
[7, 8, 9, 6, 2, 4]
Output
30
Explanation: Step-by-step explanation: We need to find the maximum sum of a monotonic subsequence in the given array. The subsequence [7, 8, 9, 6] is a monotonic increasing subsequence with a sum of 30, which is greater than the sum of [7, 8, 9]. Therefore, the maximum sum of a monotonic subsequence is 30.
Input
[2, 4, 1, 3, 5]
Output
9
Explanation: Step-by-step explanation: We need to find the maximum sum of a monotonic subsequence in the given array. The subsequence [2, 4] is a monotonic increasing subsequence with a sum of 6, but the subsequence [2] has a sum of 2, which is less than the sum of [2, 4]. Therefore, the maximum sum of a monotonic subsequence is 6.
Constraints
- 1 <= N <= 10^5
- -10^9 <= arr[i] <= 10^9
- Time Complexity expected: O(N) or O(N log N)
- Space Complexity expected: O(1) or O(N)
Optimal Approach & Strategy
Compress the array values, then iterate once while maintaining a Fenwick tree that stores the best sum for each value prefix; query and update in O(log N) per element.
Brute Force Approach
Enumerate every subsequence, check if it is monotonic, and keep the maximum sum; or use O(N^2) DP that scans all previous elements for each position.
Verified Code Solutions
function solution(nums) {
let increasing = new Array(nums.length).fill(0);
let decreasing = new Array(nums.length).fill(0);
increasing[0] = nums[0];
decreasing[0] = nums[0];
let maxSum = nums[0];
for (let i = 1; i < nums.length; i++) {
increasing[i] = Math.max(increasing[i - 1] + nums[i], nums[i]);
decreasing[i] = Math.max(decreasing[i - 1] + nums[i], nums[i]);
maxSum = Math.max(maxSum, increasing[i], decreasing[i]);
}
return maxSum;
}class Solution {
public:
int solution(vector<int>& nums) {
vector<int> increasing(nums.size());
vector<int> decreasing(nums.size());
increasing[0] = nums[0];
decreasing[0] = nums[0];
int maxSum = nums[0];
for (int i = 1; i < nums.size(); i++) {
increasing[i] = max(increasing[i - 1] + nums[i], nums[i]);
decreasing[i] = max(decreasing[i - 1] + nums[i], nums[i]);
maxSum = max(maxSum, increasing[i], decreasing[i]);
}
return maxSum;
}
};class Solution {
public int solution(int[] nums) {
int[] increasing = new int[nums.length];
int[] decreasing = new int[nums.length];
increasing[0] = nums[0];
decreasing[0] = nums[0];
int maxSum = nums[0];
for (int i = 1; i < nums.length; i++) {
increasing[i] = Math.max(increasing[i - 1] + nums[i], nums[i]);
decreasing[i] = Math.max(decreasing[i - 1] + nums[i], nums[i]);
maxSum = Math.max(maxSum, increasing[i], decreasing[i]);
}
return maxSum;
}
}def solution(nums):
increasing = [0] * len(nums)
decreasing = [0] * len(nums)
increasing[0] = nums[0]
decreasing[0] = nums[0]
max_sum = nums[0]
for i in range(1, len(nums)):
increasing[i] = max(increasing[i - 1] + nums[i], nums[i])
decreasing[i] = max(decreasing[i - 1] + nums[i], nums[i])
max_sum = max(max_sum, increasing[i], decreasing[i])
return max_sumfunction solution(nums) {
let increasing = new Array(nums.length).fill(0);
let decreasing = new Array(nums.length).fill(0);
increasing[0] = nums[0];
decreasing[0] = nums[0];
let maxSum = nums[0];
for (let i = 1; i < nums.length; i++) {
increasing[i] = Math.max(increasing[i - 1] + nums[i], nums[i]);
decreasing[i] = Math.max(decreasing[i - 1] + nums[i], nums[i]);
maxSum = Math.max(maxSum, increasing[i], decreasing[i]);
}
return maxSum;
}Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.