Given a sequence of n integers, find out the non-decreasing subsequence of length k with minimum sum. If no sequence exists output -1.
Examples :
Input: k = 3, arr[] = [58, 12, 11, 12, 82, 30, 20, 77, 16, 86]
Output: 39
Explanation: Non-decreasing subsequence of length 3 with minimum sum is: [11, 12, 16].
Input: k = 4, arr[] = [58, 12, 11, 12, 82, 30, 20, 77, 16, 86]
Output: 120
Explanation: Non-decreasing subsequence of length 4 with minimum sum is: [11, 12, 20, 77].
Table of Content
[Naive Approach] Using Simple Dynamic Programming - O(k * n2) Time O(n) Space
This can be seen as a variation of Longest Increasing Subsequence. For each index, we try all previous indices to build a non-decreasing subsequence. We maintain a DP array where
dp[i]stores the minimum sum of a subsequence of current length ending at indexi. For every length from2tok, we check allj < iand ifarr[j] <= arr[i], we update the minimum sum. Finally, the minimum value among all valid subsequences of lengthkis returned, or-1if no such subsequence exists.
#include <iostream>
using namespace std;
// function to find Min Sum Non-decreasing Subsequence of Size k
int minSum(int k, vector<int> &arr)
{
int n = arr.size();
if (k > n)
return -1;
const int INF = 1e9;
// dp[i] = min sum of subsequence of current length ending at i
vector<int> dp(n);
// Base case: length = 1
for (int i = 0; i < n; i++)
dp[i] = arr[i];
// Build for lengths 2 to k
for (int len = 2; len <= k; len++)
{
vector<int> next_dp(n, INF);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
// check non-decreasing condition
if (arr[j] <= arr[i] && dp[j] != INF)
{
next_dp[i] = min(next_dp[i], dp[j] + arr[i]);
}
}
}
dp = next_dp;
}
// get answer
int ans = INF;
for (int i = 0; i < n; i++)
ans = min(ans, dp[i]);
return ans == INF ? -1 : ans;
}
// Driver Code
int main() {
int k = 3;
vector<int> arr = {58, 12, 11, 12, 82, 30, 20, 77, 16, 86};
cout << minSum(k, arr);
return 0;
}
import java.util.Arrays;
public class GfG {
// function to find Min Sum Non-decreasing Subsequence of Size k
public static int minSum(int k, int[] arr) {
int n = arr.length;
if (k > n)
return -1;
final int INF = (int) 1e9;
// dp[i] = min sum of subsequence of current length ending at i
int[] dp = new int[n];
Arrays.fill(dp, INF);
// Base case: length = 1
for (int i = 0; i < n; i++)
dp[i] = arr[i];
// Build for lengths 2 to k
for (int len = 2; len <= k; len++) {
int[] next_dp = new int[n];
Arrays.fill(next_dp, INF);
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
// check non-decreasing condition
if (arr[j] <= arr[i] && dp[j]!= INF) {
next_dp[i] = Math.min(next_dp[i], dp[j] + arr[i]);
}
}
}
dp = next_dp;
}
// get answer
int ans = INF;
for (int i = 0; i < n; i++)
ans = Math.min(ans, dp[i]);
return ans == INF? -1 : ans;
}
// Driver Code
public static void main(String[] args) {
int k = 3;
int[] arr = {58, 12, 11, 12, 82, 30, 20, 77, 16, 86};
System.out.println(minSum(k, arr));
}
}
from typing import List
# function to find Min Sum Non-decreasing Subsequence of Size k
def minSum(k: int, arr: List[int]) -> int:
n = len(arr)
if k > n:
return -1
INF = int(1e9)
# dp[i] = min sum of subsequence of current length ending at i
dp = [arr[i] for i in range(n)]
# Build for lengths 2 to k
for length in range(2, k + 1): # FIXED
next_dp = [INF] * n
for i in range(n):
for j in range(i):
# check non-decreasing condition
if arr[j] <= arr[i] and dp[j] != INF:
next_dp[i] = min(next_dp[i], dp[j] + arr[i])
dp = next_dp
# get answer
ans = INF
for i in range(n):
ans = min(ans, dp[i])
return -1 if ans == INF else ans
# Driver Code
if __name__ == "__main__":
k = 3
arr = [58, 12, 11, 12, 82, 30, 20, 77, 16, 86]
print(minSum(k, arr))
using System;
using System.Collections.Generic;
class GfG
{
// function to find Min Sum Non-decreasing Subsequence of Size k
static int minSum(int k, int[] arr)
{
int n = arr.Length;
if (k > n)
return -1;
const int INF = 1000000000;
// dp[i] = min sum of subsequence of current length ending at i
int[] dp = new int[n];
// Base case: length = 1
for (int i = 0; i < n; i++)
dp[i] = arr[i];
// Build for lengths 2 to k
for (int len = 2; len <= k; len++)
{
int[] next_dp = new int[n];
Array.Fill(next_dp, INF);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
// check non-decreasing condition
if (arr[j] <= arr[i] && dp[j]!= INF)
{
next_dp[i] = Math.Min(next_dp[i], dp[j] + arr[i]);
}
}
}
dp = next_dp;
}
// get answer
int ans = INF;
for (int i = 0; i < n; i++)
ans = Math.Min(ans, dp[i]);
return ans == INF ? -1 : ans;
}
// Driver Code
static void Main()
{
int k = 3;
int[] arr = {58, 12, 11, 12, 82, 30, 20, 77, 16, 86};
Console.WriteLine(minSum(k, arr));
}
}
function minSum(k, arr) {
const n = arr.length;
if (k > n)
return -1;
const INF = 1e9;
// dp[i] = min sum of subsequence of current length ending at i
let dp = Array(n).fill(0);
// Base case: length = 1
for (let i = 0; i < n; i++)
dp[i] = arr[i];
// Build for lengths 2 to k
for (let len = 2; len <= k; len++) {
let next_dp = Array(n).fill(INF);
for (let i = 0; i < n; i++) {
for (let j = 0; j < i; j++) {
// check non-decreasing condition
if (arr[j] <= arr[i] && dp[j]!== INF) {
next_dp[i] = Math.min(next_dp[i], dp[j] + arr[i]);
}
}
}
dp = next_dp;
}
// get answer
let ans = INF;
for (let i = 0; i < n; i++) {
ans = Math.min(ans, dp[i]);
}
return ans === INF? -1 : ans;
}
// Driver Code
const k = 3;
const arr = [58, 12, 11, 12, 82, 30, 20, 77, 16, 86];
console.log(minSum(k, arr));
Output
39
Time Complexity: O(k * n²)
Space Complexity: O(n)
[Expected Approach] Using Dynamic Programming with Fenwick Tree - O(k * n * log n) Time O(n + m) Space
The idea is to use Dynamic Programming to efficiently find the minimum sum non-decreasing subsequence of size
k. We store the minimum sum ending at each index and, for every length, use a Fenwick Tree to quickly get the best previous valid sum (where elements are ≤ current element). This helps maintain the non-decreasing property while minimizing the sum. Finally, we return the minimum value obtained for lengthk, or-1if not possible.
Working of Approach:
- Compress the array values into ranks and use Coordinate Compression so that they can be efficiently processed using a Fenwick Tree (BIT).
- Let
dp[i]denote the minimum sum of a non-decreasing subsequence of the current length ending at indexi. For length1, initializedp[i] = arr[i]. - For every subsequence length from
2tok, use the Fenwick Tree to find the minimum DP value among all previous elements having value less than or equal toarr[i]. - If such a subsequence exists, extend it by including
arr[i]and store the new minimum sum innext_dp[i]. Update the Fenwick Tree with the current DP values for future transitions. - After processing all lengths up to
k, the minimum value in the final DP array gives the minimum sum of a non-decreasing subsequence of sizek. If no valid subsequence exists, return-1.
#include <bits/stdc++.h>
using namespace std;
// function to find Min Sum Non-decreasing Subsequence of Size k
int minSum(int k, vector<int> &arr)
{
int n = arr.size();
// Edge case: if we need a sequence longer than the array itself
if (k > n)
return -1;
if (k == 1)
{
int mn = arr[0];
for (int x : arr)
mn = min(mn, x);
return mn;
}
// 1.Coordinate Compression
vector<int> sorted_arr = arr;
sort(sorted_arr.begin(), sorted_arr.end());
sorted_arr.erase(unique(sorted_arr.begin(), sorted_arr.end()), sorted_arr.end());
int max_val = sorted_arr.size();
// Map original array values to their 1-based ranks
vector<int> rank(n);
for (int i = 0; i < n; i++)
{
rank[i] = lower_bound(sorted_arr.begin(), sorted_arr.end(), arr[i]) - sorted_arr.begin() + 1;
}
const int INF = 1e9; // Large enough to act as infinity
vector<int> dp(n);
// Base case: subsequences of length 1
for (int i = 0; i < n; i++)
{
dp[i] = arr[i];
}
// 2. Compute DP for lengths 2 to k
for (int len = 2; len <= k; len++)
{
vector<int> next_dp(n, INF);
vector<int> bit(max_val + 1, INF);
// Helper to update the BIT with the minimum value
auto update = [&](int idx, int val) {
for (; idx <= max_val; idx += idx & -idx)
{
bit[idx] = min(bit[idx], val);
}
};
// Helper to query the minimum value up to a specific rank
auto query = [&](int idx) {
int res = INF;
for (; idx > 0; idx -= idx & -idx)
{
res = min(res, bit[idx]);
}
return res;
};
for (int i = 0; i < n; i++)
{
// Query the minimum sum from the previous length where the ending value
// is <= arr[i]
int min_prev = query(rank[i]);
if (min_prev != INF)
{
next_dp[i] = arr[i] + min_prev;
}
// Update the BIT with the previously computed length's value at the
// current rank
if (dp[i] != INF)
{
update(rank[i], dp[i]);
}
}
dp = next_dp; // Move to the next length
}
// 3. Find the global minimum for length k
int ans = INF;
for (int i = 0; i < n; i++)
{
ans = min(ans, dp[i]);
}
return ans == INF ? -1 : ans;
}
// Driver Code
int main() {
int k = 3;
vector<int> arr = {58, 12, 11, 12, 82, 30, 20, 77, 16, 86};
cout << minSum(k, arr);
return 0;
}
import java.util.*;
// function to find Min Sum Non-decreasing Subsequence of Size k
public class GfG {
public static int minSum(int k, int[] arr)
{
int n = arr.length;
// Edge case: if we need a sequence longer than the array itself
if (k > n)
return -1;
if (k == 1) {
int mn = arr[0];
for (int x : arr)
mn = Math.min(mn, x);
return mn;
}
// 1. Coordinate Compression
int[] sortedArr = arr.clone();
Arrays.sort(sortedArr);
// Remove duplicates to get unique values
int uniqueCount = 0;
for (int i = 0; i < n; i++) {
if (i == 0 || sortedArr[i] != sortedArr[i - 1]) {
sortedArr[uniqueCount++] = sortedArr[i];
}
}
// Map original array values to their 1-based ranks
int[] rank = new int[n];
for (int i = 0; i < n; i++) {
rank[i] = Arrays.binarySearch(
sortedArr, 0, uniqueCount, arr[i]) + 1;
}
int INF = (int)1e9;
int[] dp = new int[n];
// Base case: subsequences of length 1
for (int i = 0; i < n; i++) {
dp[i] = arr[i];
}
// 2. Compute DP for lengths 2 to k
for (int len = 2; len <= k; len++) {
int[] next_dp = new int[n];
Arrays.fill(next_dp, INF);
int[] bit = new int[uniqueCount + 1];
Arrays.fill(bit, INF);
for (int i = 0; i < n; i++) {
int min_prev = INF;
for (int idx = rank[i]; idx > 0; idx -= idx & -idx) {
min_prev = Math.min(min_prev, bit[idx]);
}
if (min_prev != INF) {
next_dp[i] = arr[i] + min_prev;
}
if (dp[i] != INF) {
for (int idx = rank[i]; idx <= uniqueCount; idx += idx & -idx) {
bit[idx] = Math.min(bit[idx], dp[i]);
}
}
}
dp = next_dp;
}
// 3. Find the global minimum for length k
int ans = INF;
for (int i = 0; i < n; i++) {
ans = Math.min(ans, dp[i]);
}
return ans == INF ? -1 : ans;
}
// Driver Code
public static void main(String[] args)
{
int k = 3;
int[] arr = {58, 12, 11, 12, 82, 30, 20, 77, 16, 86};
System.out.println(minSum(k, arr));
}
}
from bisect import bisect_left
from typing import List
# function to find Min Sum Non-decreasing Subsequence of Size k
def minSum(k, arr):
n = len(arr)
# Edge case: if we need a sequence longer than the array itself
if k > n:
return -1
if k == 1:
return min(arr)
# 1. Coordinate Compression
sorted_unique = sorted(list(set(arr)))
max_val = len(sorted_unique)
rank_map = {val: i + 1 for i, val in enumerate(sorted_unique)}
# Map original array values to their 1-based ranks
rank = [rank_map[x] for x in arr]
INF = int(1e9) # Large enough to act as infinity
dp = arr[:] # Base case: subsequences of length 1
# 2. Compute DP for lengths 2 to k
for length in range(2, k + 1):
next_dp = [INF] * n
bit = [INF] * (max_val + 1)
# Helper to update the BIT
def update(idx, val):
while idx <= max_val:
if val < bit[idx]:
bit[idx] = val
idx += idx & -idx
# Helper to query the BIT
def query(idx):
res = INF
while idx > 0:
if bit[idx] < res:
res = bit[idx]
idx -= idx & -idx
return res
for i in range(n):
min_prev = query(rank[i])
if min_prev != INF:
next_dp[i] = arr[i] + min_prev
if dp[i] != INF:
update(rank[i], dp[i])
dp = next_dp # Move to the next length
# 3. Find the global minimum for length k
ans = min(dp)
return -1 if ans == INF else ans
# Driver Code
if __name__ == "__main__":
k = 3
arr = [58, 12, 11, 12, 82, 30, 20, 77, 16, 86]
print(minSum(k, arr))
using System;
public class GfG {
// function to find Min Sum Non-decreasing Subsequence
// of Size k
public int minSum(int k, int[] arr)
{
int n = arr.Length;
if (k > n)
return -1;
if (k == 1) {
int mn = arr[0];
foreach(int x in arr) mn = Math.Min(mn, x);
return mn;
}
int[] sortedArr = (int[])arr.Clone();
Array.Sort(sortedArr);
int uniqueCount = 0;
for (int i = 0; i < n; i++) {
if (i == 0 || sortedArr[i] != sortedArr[i - 1]) {
sortedArr[uniqueCount++] = sortedArr[i];
}
}
int[] rank = new int[n];
for (int i = 0; i < n; i++) {
rank[i] = Array.BinarySearch(sortedArr, 0, uniqueCount, arr[i]) + 1;
}
int INF = (int)1e9;
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
dp[i] = arr[i];
}
for (int len = 2; len <= k; len++) {
int[] next_dp = new int[n];
Array.Fill(next_dp, INF);
int[] bit = new int[uniqueCount + 1];
Array.Fill(bit, INF);
for (int i = 0; i < n; i++) {
int min_prev = INF;
for (int idx = rank[i]; idx > 0; idx -= idx & -idx) {
min_prev = Math.Min(min_prev, bit[idx]);
}
if (min_prev != INF) {
next_dp[i] = arr[i] + min_prev;
}
if (dp[i] != INF) {
for (int idx = rank[i]; idx <= uniqueCount; idx += idx & -idx) {
bit[idx] = Math.Min(bit[idx], dp[i]);
}
}
}
dp = next_dp;
}
int ans = INF;
for (int i = 0; i < n; i++) {
ans = Math.Min(ans, dp[i]);
}
return ans == INF ? -1 : ans;
}
// Driver Code
public static void Main()
{
int k = 3;
int[] arr = { 58, 12, 11, 12, 82, 30, 20, 77, 16, 86 };
GfG obj = new GfG();
Console.WriteLine(obj.minSum(k, arr));
}
}
// function to find Min Sum Non-decreasing Subsequence of
// Size k
function minSum(k, arr)
{
let n = arr.length;
// Edge case: if we need a sequence longer than the
// array itself
if (k > n)
return -1;
if (k === 1) {
let mn = arr[0];
for (let x of arr)
mn = Math.min(mn, x);
return mn;
}
// 1. Coordinate Compression
let sorted_arr = arr.slice().sort((a, b) => a - b);
sorted_arr = [...new Set(sorted_arr) ];
let max_val = sorted_arr.length;
// Map original array values to their 1-based ranks
let rank = new Array(n);
for (let i = 0; i < n; i++) {
rank[i] = sorted_arr.indexOf(arr[i]) + 1;
}
const INF = 1e9; // Large enough to act as infinity
let dp = new Array(n);
// Base case: subsequences of length 1
for (let i = 0; i < n; i++) {
dp[i] = arr[i];
}
// 2. Compute DP for lengths 2 to k
for (let len = 2; len <= k; len++) {
let next_dp = new Array(n).fill(INF);
let bit = new Array(max_val + 1).fill(INF);
// Helper to update the BIT with the minimum value
const update = (idx, val) => {
for (; idx <= max_val; idx += idx & -idx) {
bit[idx] = Math.min(bit[idx], val);
}
};
// Helper to query the minimum value up to a
// specific rank
const query = (idx) => {
let res = INF;
for (; idx > 0; idx -= idx & -idx) {
res = Math.min(res, bit[idx]);
}
return res;
};
for (let i = 0; i < n; i++) {
// Query the minimum sum from the previous
// length where the ending value is <= arr[i]
let min_prev = query(rank[i]);
if (min_prev !== INF) {
next_dp[i] = arr[i] + min_prev;
}
// Update the BIT with the previously computed
// length's value at the current rank
if (dp[i] !== INF) {
update(rank[i], dp[i]);
}
}
dp = next_dp; // Move to the next length
}
// 3. Find the global minimum for length k
let ans = INF;
for (let i = 0; i < n; i++) {
ans = Math.min(ans, dp[i]);
}
return ans === INF ? -1 : ans;
}
// Driver Code
const k = 3;
const arr = [ 58, 12, 11, 12, 82, 30, 20, 77, 16, 86 ];
console.log(minSum(k, arr));
Output
39
Time Complexity: O(k * n * log n)
Space Complexity: O(n + m)