Formatted question description: https://leetcode.ca/all/1425.html

# 1425. Constrained Subsequence Sum (Hard)

Given an integer array nums and an integer k, return the maximum sum of a non-empty subsequence of that array such that for every two consecutive integers in the subsequence, nums[i] and nums[j], where i < j, the condition j - i <= k is satisfied.

subsequence of an array is obtained by deleting some number of elements (can be zero) from the array, leaving the remaining elements in their original order.

Example 1:

Input: nums = [10,2,-10,5,20], k = 2
Output: 37
Explanation: The subsequence is [10, 2, 5, 20].

Example 2:

Input: nums = [-1,-2,-3], k = 1
Output: -1
Explanation: The subsequence must be non-empty, so we choose the largest number.

Example 3:

Input: nums = [10,-2,-10,-5,20], k = 2
Output: 23
Explanation: The subsequence is [10, -2, -5, 20].

Constraints:

• 1 <= k <= nums.length <= 10^5
• -10^4 <= nums[i] <= 10^4

Related Topics:
Dynamic Programming

## Solution 1. DP

If all the elements in A are zero or negative, the result is just the maximum value.

Otherwise, let dp[i] be the maximum result I can get on the subarray A[0..i] if I pick A[i].

The result is max(dp[i] | 0 <= i < N).

For dp[i], since we picked A[i], the previous pick must be one of A[i-k], A[i-k+1], …, A[i-1].

So dp[i] = A[i] + max( dp[j] | i-k <= j < i ).

Here is a TLE version.

// OJ: https://leetcode.com/problems/constrained-subsequence-sum/

// Time: O(NK)
// Space: O(N)
// NOTE: this will get TLE
class Solution {
public:
int constrainedSubsetSum(vector<int>& A, int k) {
int maxVal = *max_element(begin(A), end(A));
if (maxVal <= 0) return maxVal;
int N = A.size(), ans = 0;
vector<int> dp(N);
for (int i = 0; i < N; ++i) {
int mx = 0;
for (int j = 1; j <= k; ++j) {
if (i - j < 0) break;
mx = max(mx, dp[i - j]);
}
dp[i] = mx + A[i];
ans = max(ans, dp[i]);
}
return ans;
}
};

One optimization is that we can break the j loop once A[i - j] > 0, because further extending j won’t give us better result.

// OJ: https://leetcode.com/problems/constrained-subsequence-sum/

// Time: O(NK)
// Space: O(N)
class Solution {
public:
int constrainedSubsetSum(vector<int>& A, int k) {
int maxVal = *max_element(begin(A), end(A));
if (maxVal <= 0) return maxVal;
int N = A.size(), ans = 0;
vector<int> dp(N);
for (int i = 0; i < N; ++i) {
int mx = 0;
for (int j = 1; j <= k; ++j) {
if (i - j < 0) break;
mx = max(mx, dp[i - j]);
if (A[i - j] > 0) break;
}
dp[i] = mx + A[i];
ans = max(ans, dp[i]);
}
return ans;
}
};

## Solution 2. DP + multiset

Same idea as Solution 1, here we use a multiset to prevent linear search of the maximum dp value.

// OJ: https://leetcode.com/problems/constrained-subsequence-sum/

// Time: O(NlogK)
// Space: O(N)
class Solution {
public:
int constrainedSubsetSum(vector<int>& A, int k) {
int maxVal = *max_element(begin(A), end(A));
if (maxVal <= 0) return maxVal;
int N = A.size(), ans = 0;
multiset<int> s{0};
vector<int> dp(N);
for (int i = 0; i < N; ++i) {
int sum = *s.rbegin() + A[i];
if (i >= k) s.erase(s.find(dp[i - k]));
s.insert(sum);
dp[i] = sum;
ans = max(ans, sum);
}
return ans;
}
};

## Solution 3. DP + Monotonous Deque

Assume the dp values are 3, 1, 2. Popping 3 will change the max value, but popping 1 won’t. This tells us that we can just keep the 3, 2 dp sequence which forms a monotonically decreasing sequence.

We can store the indexes of these sequence in a deque<int> mx. dp[mx.front()] stores the maximum dp value within the dp[i-k] and dp[i-1] range.

dp[i] = max(0, dp[mx.front()]) + A[i]

Given dp[i], we can pop those indexes in mx which are pointing to dp values that are smaller or equal todp[i].

Then we can push i into the mx deque.

Don’t forget popping the out-of-range index, i.e. when mx.front() === i - k - 1, we should pop front.

// OJ: https://leetcode.com/problems/constrained-subsequence-sum/

// Time: O(N)
// Space: O(N)
class Solution {
public:
int constrainedSubsetSum(vector<int>& A, int k) {
int maxVal = *max_element(begin(A), end(A));
if (maxVal <= 0) return maxVal;
int N = A.size(), ans = 0;
deque<int> mx;
vector<int> dp(N);
for (int i = 0; i < N; ++i) {
if (mx.size() && mx.front() == i - k - 1) mx.pop_front();
dp[i] = max(0, mx.size() ? dp[mx.front()] : 0) + A[i];
while (mx.size() && dp[mx.back()] <= dp[i]) mx.pop_back();
mx.push_back(i);
ans = max(ans, dp[i]);
}
return ans;
}
};

## Solution 4. DP + Monotonous Deque

// OJ: https://leetcode.com/problems/constrained-subsequence-sum/

// Time: O(N)
// Space: O(K)
// Ref: https://leetcode.com/problems/constrained-subsequence-sum/discuss/597751/JavaC%2B%2BPython-O(N)-Decreasing-Deque
class Solution {
public:
int constrainedSubsetSum(vector<int>& A, int k) {
deque<int> q;
int ans = A[0];
for (int i = 0; i < A.size(); ++i) {
A[i] += q.size() ? q.front() : 0;
ans = max(ans, A[i]);
while (q.size() && A[i] > q.back()) q.pop_back();
if (A[i] > 0) q.push_back(A[i]);
if (i >= k && q.size() && q.front() == A[i - k]) q.pop_front();
}
return ans;
}
};

Java

class Solution {
public int constrainedSubsetSum(int[] nums, int k) {
int length = nums.length;
int max = nums[0];
for (int i = 1; i < length; i++)
max = Math.max(max, nums[i]);
if (max <= 0)
return max;
if (k == length)
k--;
ArrayDeque<Integer> deque = new ArrayDeque<Integer>();
int[][] dp = new int[length][2];
dp[0][0] = nums[0];
dp[0][1] = 0;
for (int i = 1; i <= k; i++) {
int num = nums[i];
dp[i][0] = Math.max(num, dp[deque.getFirst()][0] + num);
while (!deque.isEmpty() && dp[deque.getLast()][0] < dp[i][0])
deque.removeLast();
dp[i][1] = Math.max(dp[i - 1][0], dp[i - 1][1]);
}
for (int i = k + 1; i < length; i++) {
if (!deque.isEmpty() && deque.getFirst() == i - k - 1)
deque.removeFirst();
int num = nums[i];
dp[i][0] = Math.max(num, dp[deque.getFirst()][0] + num);
while (!deque.isEmpty() && dp[deque.getLast()][0] < dp[i][0])
deque.removeLast();