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>();
        deque.addLast(0);
        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();
            deque.addLast(i);
            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();
            deque.addLast(i);
            dp[i][1] = Math.max(dp[i - 1][0], dp[i - 1][1]);
        }
        return Math.max(dp[length - 1][0], dp[length - 1][1]);
    }
}

All Problems

All Solutions