Given an array, find the maximum possible sum of non-decreasing subsequence of length k. If not possible return -1.
Examples:
Input : a = {8, 5, 9, 10, 5, 6, 21, 8}, k = 3
Output : 40
Explanation: Possible Increasing subsequence of Length 3 with maximum possible sum is 9 + 10 + 21 = 40Input : a = {10, 5}, k = 2
Output : -1
Explanation : Can't make any increasing subsequence of length 2.
Note: This problem is a simple variation of Longest Increasing Subsequence. If you are unknown of how to calculate the longest increasing subsequence then see the implementation going to the link.
Table of Content
[Naive Approach] Using Recursion : O(2 ^ n) Time and O(n) Space
The intuition of this code is to generate all possible subsequences using recursion and choose the one having maximum sum with exactly k elements. At every index, we have two choices: either take the current element or skip it. An element is taken only if it keeps the subsequence non-decreasing (current element >= previous selected element). Finally, among all valid subsequences of size k, we return the maximum possible sum.
- Start recursion from index 0 with no previous element selected and required subsequence size k.
- At each index, either take the current element if it maintains non-decreasing order, or skip the current element.
- If exactly k elements are selected, return 0 as a valid subsequence is formed.
- If the array ends before selecting k elements, return negative infinity (INT_MIN).
- Return the maximum sum obtained from both choices (take and not take).
#include <bits/stdc++.h>
using namespace std;
long long solve(int i, int prev, int k, int n, vector<int> &nums)
{
// Base Case:
// If we have selected exactly k elements
if (k == 0)
{
return 0;
}
// If array ends before selecting k elements
if (i == n)
{
return INT_MIN;
}
// Option 1: Take current element
long long take = INT_MIN;
// Current element can be taken if:
// 1. No previous element selected
// 2. Current element is >= previous element
if (prev == -1 || nums[i] >= prev)
{
long long next = solve(i + 1, nums[i], k - 1, n, nums);
// Add current value only if next state is valid
if (next != INT_MIN)
{
take = nums[i] + next;
}
}
// Option 2: Skip current element
long long not_take = solve(i + 1, prev, k, n, nums);
// Return maximum of both choices
return max(take, not_take);
}
int maxSum(vector<int> &a, int k)
{
int n = a.size();
long long ans = solve(0, -1, k, n, a);
// If no valid subsequence exists
return (ans < 0) ? -1 : ans;
}
int main()
{
vector<int> a = {8, 5, 9, 10, 5, 6, 21, 8};
int k = 3;
// Function call
int result = maxSum(a, k);
// Output result
cout << result << endl;
return 0;
}
import java.util.*;
public class GFG {
static long solve(int i, int prev, int k, int n,
int[] nums)
{
// Base Case:
// If we have selected exactly k elements
if (k == 0) {
return 0;
}
// If array ends before selecting k elements
if (i == n) {
return Integer.MIN_VALUE;
}
// Option 1: Take current element
long take = Integer.MIN_VALUE;
// Current element can be taken if:
// 1. No previous element selected
// 2. Current element is >= previous element
if (prev == -1 || nums[i] >= prev) {
long next
= solve(i + 1, nums[i], k - 1, n, nums);
// Add current value only if next state is valid
if (next != Integer.MIN_VALUE) {
take = nums[i] + next;
}
}
// Option 2: Skip current element
long notTake = solve(i + 1, prev, k, n, nums);
// Return maximum of both choices
return Math.max(take, notTake);
}
static int maxSum(int[] a, int k)
{
int n = a.length;
long ans = solve(0, -1, k, n, a);
// If no valid subsequence exists
return (ans < 0) ? -1 : (int)ans;
}
public static void main(String[] args)
{
int[] a = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(a, k);
// Output result
System.out.println(result);
}
}
import sys
def solve(i, prev, k, n, nums):
# Base Case:
# If we have selected exactly k elements
if k == 0:
return 0
# If array ends before selecting k elements
if i == n:
return -sys.maxsize
# Option 1: Take current element
take = -sys.maxsize
# Current element can be taken if:
# 1. No previous element selected
# 2. Current element is >= previous element
if prev == -1 or nums[i] >= prev:
next_value = solve(i + 1, nums[i], k - 1, n, nums)
# Add current value only if next state is valid
if next_value != -sys.maxsize:
take = nums[i] + next_value
# Option 2: Skip current element
not_take = solve(i + 1, prev, k, n, nums)
# Return maximum of both choices
return max(take, not_take)
def maxSum(a, k):
n = len(a)
ans = solve(0, -1, k, n, a)
# If no valid subsequence exists
return -1 if ans < 0 else ans
# Driver Code
if __name__ == "__main__":
a = [8, 5, 9, 10, 5, 6, 21, 8]
k = 3
# Function call
result = maxSum(a, k)
# Output result
print(result)
using System;
class GFG {
static long solve(int i, int prev, int k, int n, int[] nums)
{
// Base Case:
// If we have selected exactly k elements
if (k == 0) {
return 0;
}
// If array ends before selecting k elements
if (i == n) {
return int.MinValue;
}
// Option 1: Take current element
long take = int.MinValue;
// Current element can be taken if:
// 1. No previous element selected
// 2. Current element is >= previous element
if (prev == -1 || nums[i] >= prev) {
long next =
solve(i + 1, nums[i], k - 1, n, nums);
// Add current value only if next state is valid
if (next != int.MinValue) {
take = nums[i] + next;
}
}
// Option 2: Skip current element
long notTake = solve(i + 1, prev, k, n, nums);
// Return maximum of both choices
return Math.Max(take, notTake);
}
static int maxSum(int[] arr, int k)
{
int n = arr.Length;
long ans = solve(0, -1, k, n, arr);
// If no valid subsequence exists
return (ans < 0) ? -1 : (int)ans;
}
static void Main()
{
// Creating array
int[] arr = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
Console.WriteLine(result);
}
}
function solve(i, prev, k, n, nums)
{
// Base Case:
// If we have selected exactly k elements
if (k === 0) {
return 0;
}
// If array ends before selecting k elements
if (i === n) {
return Number.MIN_SAFE_INTEGER;
}
// Option 1: Take current element
let take = Number.MIN_SAFE_INTEGER;
// Current element can be taken if:
// 1. No previous element selected
// 2. Current element is >= previous element
if (prev === -1 || nums[i] >= prev) {
let next = solve(i + 1, nums[i], k - 1, n, nums);
// Add current value only if next state is valid
if (next !== Number.MIN_SAFE_INTEGER) {
take = nums[i] + next;
}
}
// Option 2: Skip current element
let notTake = solve(i + 1, prev, k, n, nums);
// Return maximum of both choices
return Math.max(take, notTake);
}
function maxSum(a, k)
{
let n = a.length;
let ans = solve(0, -1, k, n, a);
// If no valid subsequence exists
return (ans < 0) ? -1 : ans;
}
// Driver Code
let a = [ 8, 5, 9, 10, 5, 6, 21, 8 ];
let k = 3;
// Function call
let result = maxSum(a, k);
// Output result
console.log(result);
Output
40

[Expected Approach] Using Memoization(DP) - O(n ^ 2 * k) Time and O(n * k) Space
The main intuition behind this approach is that for every index we have only two choices: either include the current element in the subsequence or skip it. While making these choices recursively, many states get recomputed multiple times, which makes the recursive solution exponential. Since, we have repeating and overlapping subproblems, we can use Memoization(DP) thus, reducing the time complexity from exponential to polynomial.
#include <bits/stdc++.h>
using namespace std;
long long solve(int i, int prev, int k, int n, vector<int> &nums, vector<vector<vector<long long>>> &dp)
{
// Base Case:
// If exactly k elements are selected
if (k == 0)
{
return 0;
}
// If array ends before selecting k elements
if (i == n)
{
return INT_MIN;
}
// If already computed
if (dp[i][prev + 1][k] != -1)
{
return dp[i][prev + 1][k];
}
// Option 1: Do not take current element
long long not_take = solve(i + 1, prev, k, n, nums, dp);
// Option 2: Take current element
long long take = INT_MIN;
// Current element can be selected if:
// 1. No previous element selected
// 2. nums[i] >= previous selected value
if (prev == -1 || nums[i] >= nums[prev])
{
long long next = solve(i + 1, i, k - 1, n, nums, dp);
// Add current value only if next state is valid
if (next != INT_MIN)
{
take = nums[i] + next;
}
}
// Store and return answer
return dp[i][prev + 1][k] = max(take, not_take);
}
int maxSum(vector<int> &a, int k)
{
int n = a.size();
vector<vector<vector<long long>>> dp(n, vector<vector<long long>>(n + 1, vector<long long>(k + 1, -1)));
long long ans = solve(0, -1, k, n, a, dp);
// If no valid subsequence exists
return (ans < 0) ? -1 : ans;
}
int main()
{
vector<int> arr = {8, 5, 9, 10, 5, 6, 21, 8};
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
cout << result << endl;
return 0;
}
import java.util.*;
public class GFG {
static long solve(int i, int prev, int k, int n,
int[] nums, long[][][] dp)
{
// Base Case:
// If exactly k elements are selected
if (k == 0) {
return 0;
}
// If array ends before selecting k elements
if (i == n) {
return Integer.MIN_VALUE;
}
// If already computed
if (dp[i][prev + 1][k] != -1) {
return dp[i][prev + 1][k];
}
// Option 1: Do not take current element
long notTake
= solve(i + 1, prev, k, n, nums, dp);
// Option 2: Take current element
long take = Integer.MIN_VALUE;
// Current element can be selected if:
// 1. No previous element selected
// 2. nums[i] >= previous selected value
if (prev == -1 || nums[i] >= nums[prev]) {
long next = solve(i + 1, i, k - 1, n, nums, dp);
// Add current value only if next state is valid
if (next != Integer.MIN_VALUE) {
take = nums[i] + next;
}
}
// Store and return answer
return dp[i][prev + 1][k]
= Math.max(take, notTake);
}
static int maxSum(int[] a, int k)
{
int n = a.length;
long[][][] dp = new long[n][n + 1][k + 1];
// Initialize DP array with -1
for (int i = 0; i < n; i++) {
for (int j = 0; j <= n; j++) {
Arrays.fill(dp[i][j], -1);
}
}
long ans = solve(0, -1, k, n, a, dp);
// If no valid subsequence exists
return (ans < 0) ? -1 : (int)ans;
}
public static void main(String[] args)
{
int[] arr = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
System.out.println(result);
}
}
import sys
def solve(i, prev, k, n, nums, dp):
# Base Case:
# If exactly k elements are selected
if k == 0:
return 0
# If array ends before selecting k elements
if i == n:
return -sys.maxsize
# If already computed
if dp[i][prev + 1][k] != -1:
return dp[i][prev + 1][k]
# Option 1: Do not take current element
not_take = solve(i + 1, prev,
k, n, nums, dp)
# Option 2: Take current element
take = -sys.maxsize
# Current element can be selected if:
# 1. No previous element selected
# 2. nums[i] >= previous selected value
if (prev == -1 or
nums[i] >= nums[prev]):
next_value = solve(i + 1, i, k - 1, n, nums, dp)
# Add current value only if next state is valid
if next_value != -sys.maxsize:
take = nums[i] + next_value
# Store and return answer
dp[i][prev + 1][k] = max(take, not_take)
return dp[i][prev + 1][k]
def maxSum(a, k):
n = len(a)
dp = [[[-1 for _ in range(k + 1)]
for _ in range(n + 1)]
for _ in range(n)]
ans = solve(0, -1, k, n, a, dp)
# If no valid subsequence exists
return -1 if ans < 0 else ans
# Driver Code
if __name__ == "__main__":
arr = [8, 5, 9, 10, 5, 6, 21, 8]
k = 3
# Function call
result = maxSum(arr, k)
# Output result
print(result)
using System;
class GFG {
static long Solve(int i, int prev, int k, int n,
int[] nums, long[][][] dp)
{
// Base Case:
// If exactly k elements are selected
if (k == 0) {
return 0;
}
// If array ends before selecting k elements
if (i == n) {
return int.MinValue;
}
// If already computed
if (dp[i][prev + 1][k] != -1) {
return dp[i][prev + 1][k];
}
// Option 1: Do not take current element
long notTake = Solve(i + 1, prev, k, n, nums, dp);
// Option 2: Take current element
long take = int.MinValue;
// Current element can be selected if :
// 1. No previous element selected
// 2. nums[i] >= previous selected value
if (prev == -1 || nums[i] >= nums[prev]) {
long next = Solve(i + 1, i, k - 1, n, nums, dp);
// Add current value only if next state is valid
if (next != int.MinValue) {
take = nums[i] + next;
}
}
// Store and return answer
dp[i][prev + 1][k] = Math.Max(take, notTake);
return dp[i][prev + 1][k];
}
static int maxSum(int[] arr, int k)
{
int n = arr.Length;
// Initialize DP array with -1
long[][][] dp = new long[n][][];
for (int i = 0; i < n; i++) {
dp[i] = new long[n + 1][];
for (int j = 0; j <= n; j++) {
dp[i][j] = new long[k + 1];
for (int l = 0; l <= k; l++) {
dp[i][j][l] = -1;
}
}
}
long ans = Solve(0, -1, k, n, arr, dp);
// If no valid subsequence exists
return (ans < 0) ? -1 : (int)ans;
}
static void Main()
{
int[] arr = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
Console.WriteLine(result);
}
}
function solve(i, prev, k, n, nums, dp)
{
// Base Case:
// If exactly k elements are selected
if (k === 0) {
return 0;
}
// If array ends before selecting k elements
if (i === n) {
return Number.MIN_SAFE_INTEGER;
}
// If already computed
if (dp[i][prev + 1][k] !== -1) {
return dp[i][prev + 1][k];
}
// Option 1: Do not take current element
let notTake = solve(i + 1, prev, k, n, nums, dp);
// Option 2: Take current element
let take = Number.MIN_SAFE_INTEGER;
// Current element can be selected if:
// 1. No previous element selected
// 2. nums[i] >= previous selected value
if (prev === -1 || nums[i] >= nums[prev]) {
let next = solve(i + 1, i, k - 1, n, nums, dp);
// Add current value only if next state is valid
if (next !== Number.MIN_SAFE_INTEGER) {
take = nums[i] + next;
}
}
// Store and return answer
dp[i][prev + 1][k] = Math.max(take, notTake);
return dp[i][prev + 1][k];
}
function maxSum(a, k)
{
let n = a.length;
let dp = Array.from(
{length : n},
() => Array.from({length : n + 1},
() => Array(k + 1).fill(-1)));
let ans = solve(0, -1, k, n, a, dp);
// If no valid subsequence exists
return (ans < 0) ? -1 : ans;
}
// Driver Code
let arr = [ 8, 5, 9, 10, 5, 6, 21, 8 ];
let k = 3;
// Function call
let result = maxSum(arr, k);
// Output result
console.log(result);
Output
40
[Expected Approach] Using Bottom Up(DP) - O(n ^ 2 * k) Time and O(n * k) Space
In the bottom-up approach, we iteratively build solutions for smaller subsequences first and then use them to construct larger subsequences. The DP table helps us keep track of the maximum sum of non-decreasing subsequences of different lengths ending at every index, which finally allows us to compute the required answer efficiently.
Consider the following example for better understanding: arr = {8, 5, 9}, k = 2
- Initialize DP table : Base Case --> Sequence of length 1:
dp[0][1] = 8
dp[1][1] = 5
dp[2][1] = 9 - Subsequence of length 2:
- For i = 0 ---> No j < i so no update
- For i = 1, j = 0 ---> check: arr[1] >= arr[0] ---> 5 >= 8 ---> False ---> No update
- For i = 2, j = 0 ---> check: arr[2] >= arr[0] ---> 9 >= 8 ---> True
dp[2][2] = max(-INF, dp[0][1] + 9) = max(-INF, 8 + 9) = 17 - For i = 2, j = 1 ---> check: arr[2] >= arr[1] ---> 9 >= 5 ---> True
dp[2][2] = max(17, dp[1][1] + 9) = max(17, 5 + 9) = 17 - Traverse the DP table to find answer --> Maximum value among dp[i][2] = 17
- Required Non-Decreasing Subsequence: {8, 9}
Final answer : 17
#include <bits/stdc++.h>
using namespace std;
int maxSum(vector<int> &a, int k)
{
int n = a.size();
// Edge Case:
// If k is greater than array size
if (k > n)
{
return -1;
}
// dp[i][j] represents: Maximum sum of non-decreasing subsequence
// of size j ending at index i
vector<vector<long long>> dp(n, vector<long long>(k + 1, INT_MIN));
// Base Case: Subsequence of length 1 ending at index i
// will simply contain a[i]
for (int i = 0; i < n; i++)
{
dp[i][1] = a[i];
}
for (int len = 2; len <= k; len++)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
// Current element can extend previous subsequence only if:
// a[i] >= a[j]
if (a[i] >= a[j] && dp[j][len - 1] != INT_MIN)
{
dp[i][len] = max(dp[i][len], dp[j][len - 1] + a[i]);
}
}
}
}
// Find maximum sum among all subsequences of size k
long long ans = INT_MIN;
for (int i = 0; i < n; i++)
{
ans = max(ans, dp[i][k]);
}
// If no valid subsequence exists
return (ans == INT_MIN) ? -1 : ans;
}
int main()
{
vector<int> arr = {8, 5, 9, 10, 5, 6, 21, 8};
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
cout << result << endl;
return 0;
}
import java.util.*;
public class GFG {
static int maxSum(int[] a, int k)
{
int n = a.length;
// Edge Case:
// If k is greater than array size
if (k > n) {
return -1;
}
/*
dp[i][j] represents:
Maximum sum of non-decreasing subsequence
of size j ending at index i
*/
long[][] dp = new long[n][k + 1];
// Initialize DP table with minimum value
for (int i = 0; i < n; i++) {
Arrays.fill(dp[i], Integer.MIN_VALUE);
}
/*
Base Case:
Subsequence of length 1 ending at index i
will simply contain a[i]
*/
for (int i = 0; i < n; i++) {
dp[i][1] = a[i];
}
for (int len = 2; len <= k; len++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
/*
Current element can extend
previous subsequence only if:
a[i] >= a[j]
*/
if (a[i] >= a[j]
&& dp[j][len - 1]
!= Integer.MIN_VALUE) {
dp[i][len] = Math.max(dp[i][len],
dp[j][len - 1]
+ a[i]);
}
}
}
}
// Find maximum sum among all subsequences of size k
long ans = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
ans = Math.max(ans, dp[i][k]);
}
// If no valid subsequence exists
return (ans == Integer.MIN_VALUE) ? -1 : (int)ans;
}
public static void main(String[] args)
{
int[] arr = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
System.out.println(result);
}
}
import sys
def maxSum(a, k):
n = len(a)
# Edge Case:
# If k is greater than array size
if k > n:
return -1
"""
dp[i][j] represents:
Maximum sum of non-decreasing subsequence
of size j ending at index i
"""
dp = [[-sys.maxsize for _ in range(k + 1)] for _ in range(n)]
"""
Base Case:
Subsequence of length 1 ending at index i
will simply contain a[i]
"""
for i in range(n):
dp[i][1] = a[i]
for length in range(2, k + 1):
for i in range(n):
for j in range(i):
"""
Current element can extend
previous subsequence only if:
a[i] >= a[j]
"""
if a[i] >= a[j] and dp[j][length - 1] != -sys.maxsize:
dp[i][length] = max(dp[i][length],
dp[j][length - 1] + a[i])
# Find maximum sum among all subsequences of size k
ans = -sys.maxsize
for i in range(n):
ans = max(ans, dp[i][k])
# If no valid subsequence exists
return -1 if ans == -sys.maxsize else ans
# Driver Code
if __name__ == "__main__":
arr = [8, 5, 9, 10, 5, 6, 21, 8]
k = 3
# Function call
result = maxSum(arr, k)
# Output result
print(result)
using System;
class GFG {
static int maxSum(int[] arr, int k)
{
int n = arr.Length;
// Edge Case:
// If k is greater than array size
if (k > n) {
return -1;
}
/*
dp[i][j] represents:
Maximum sum of non-decreasing subsequence
of size j ending at index i
*/
long[, ] dp = new long[n, k + 1];
// Initialize DP table with minimum value
for (int i = 0; i < n; i++) {
for (int j = 0; j <= k; j++) {
dp[i, j] = int.MinValue;
}
}
/*
Base Case:
Subsequence of length 1 ending at index i
will simply contain arr[i]
*/
for (int i = 0; i < n; i++) {
dp[i, 1] = arr[i];
}
for (int len = 2; len <= k; len++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
/*
Current element can extend
previous subsequence only if:
arr[i] >= arr[j]
*/
if (arr[i] >= arr[j]
&& dp[j, len - 1] != int.MinValue) {
dp[i, len] = Math.Max(dp[i, len],
dp[j, len - 1]
+ arr[i]);
}
}
}
}
// Find maximum sum among all subsequences of size k
long ans = int.MinValue;
for (int i = 0; i < n; i++) {
ans = Math.Max(ans, dp[i, k]);
}
// If no valid subsequence exists
return (ans == int.MinValue) ? -1 : (int)ans;
}
static void Main()
{
int[] arr = { 8, 5, 9, 10, 5, 6, 21, 8 };
int k = 3;
// Function call
int result = maxSum(arr, k);
// Output result
Console.WriteLine(result);
}
}
function maxSum(a, k)
{
let n = a.length;
// Edge Case:
// If k is greater than array size
if (k > n) {
return -1;
}
/*
dp[i][j] represents:
Maximum sum of non-decreasing subsequence
of size j ending at index i
*/
let dp = Array.from(
{length : n},
() => Array(k + 1).fill(Number.MIN_SAFE_INTEGER));
/*
Base Case:
Subsequence of length 1 ending at index i
will simply contain a[i]
*/
for (let i = 0; i < n; i++) {
dp[i][1] = a[i];
}
for (let len = 2; len <= k; len++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < i; j++) {
/*
Current element can extend
previous subsequence only if:
a[i] >= a[j]
*/
if (a[i] >= a[j]
&& dp[j][len - 1]
!== Number.MIN_SAFE_INTEGER) {
dp[i][len] = Math.max(
dp[i][len], dp[j][len - 1] + a[i]);
}
}
}
}
// Find maximum sum among all subsequences of size k
let ans = Number.MIN_SAFE_INTEGER;
for (let i = 0; i < n; i++) {
ans = Math.max(ans, dp[i][k]);
}
// If no valid subsequence exists
return (ans === Number.MIN_SAFE_INTEGER) ? -1 : ans;
}
// Driver Code
let arr = [ 8, 5, 9, 10, 5, 6, 21, 8 ];
let k = 3;
// Function call
let result = maxSum(arr, k);
// Output result
console.log(result);
Output
40