Maximum Sum Increasing Subsequence (original) (raw)

Given an array **arr[] which consists ofpositive integers. Find the sum of the maximum sum subsequence of the given array such that the integers in the subsequence are sorted in strictly increasing order.

**Examples:

**Input: arr[] = [1, 101, 2, 3, 100]
**Output: 106
**Explanation: The maximum sum of a increasing sequence is obtained from [1, 2, 3, 100].

**Input: arr[] = [4, 1, 2, 3]
**Output: 6
**Explanation: The maximum sum of a increasing sequence is obtained from [1, 2, 3].

Table of Content

[Naive Approach] Using Recursion - O(2^n) Time and O(n) Space

This problem is a variation of the standard Longest Increasing Subsequence (LIS) problem. In this problem, we will consider the sum of subsequence instead of its length.

In this approach, we recursively explore all possible subsequences. For each index i, we decide whether to include it in the subsequence based on the last chosen index j. If a[i] > a[j], we have two choices — either include a[i] or skip it and we take the option that gives the maximum sum. If a[i] <= a[j], we cannot include a[i], so we simply move to the next index.

C++ `

//Driver Code Starts #include #include using namespace std; //Driver Code Ends

int findMaxSum(vector& arr, int i, int j) { if(i == arr.size() + 1) return 0;

// i - current index(1-based index)
// j - last chosen index(1-based index)
// if j = 0, no element is chosen
if(j == 0 || arr[i-1] > arr[j-1]) {
    return max(arr[i-1] + findMaxSum(arr, i+1, i), 
               findMaxSum(arr, i+1, j));
}

// if current element <= last chosen element
// we can't chose current element
else return findMaxSum(arr, i+1, j);

}

int maxSumIS(vector& arr) { int n = arr.size(); return findMaxSum(arr, 1, 0); }

//Driver Code Starts

int main() { vector arr = {1, 101, 2, 3, 100}; cout << maxSumIS(arr); } //Driver Code Ends

Java

//Driver Code Starts class GFG { //Driver Code Ends

static int findMaxSum(int[] arr, int i, int j) {
    if(i == arr.length + 1) return 0;
    
    // i - current index(1-based index)
    // j - last chosen index(1-based index)
    // if j = 0, no element is chosen
    if(j == 0 || arr[i-1] > arr[j-1]) {
        return Math.max(arr[i-1] + findMaxSum(arr, i+1, i), 
                                   findMaxSum(arr, i+1, j));
    }
    // if current element <= last chosen element
    // we can't chose current element
    else return findMaxSum(arr, i+1, j);
}

static int maxSumIS(int arr[]) {
    int n = arr.length;
    return findMaxSum(arr, 1, 0);
}

//Driver Code Starts public static void main(String[] args) { int[] arr = {1, 101, 2, 3, 100}; System.out.println(maxSumIS(arr)); } }

//Driver Code Ends

Python

def findMaxSum(arr, i, j): if i == len(arr) + 1: return 0

# i - current index(1-based index)
# j - last chosen index(1-based index)
# if j = 0, no element is chosen
if j == 0 or arr[i - 1] > arr[j - 1]:
    return max(arr[i - 1] + findMaxSum(arr, i + 1, i),
               findMaxSum(arr, i + 1, j))
# if current element <= last chosen element
# we can't chose current element
else:
    return findMaxSum(arr, i + 1, j)

def maxSumIS(arr): n = len(arr) return findMaxSum(arr, 1, 0)

#Driver Code Starts

if name == "main": arr = [1, 101, 2, 3, 100] print(maxSumIS(arr)) #Driver Code Ends

C#

//Driver Code Starts using System;

class GFG { //Driver Code Ends

static int findMaxSum(int[] arr, int i, int j) {
    if(i == arr.Length + 1) return 0;
    
    // i - current index(1-based index)
    // j - last chosen index(1-based index)
    // if j = 0, no element is chosen
    if(j == 0 || arr[i-1] > arr[j-1]) {
        return Math.Max(arr[i-1] + findMaxSum(arr, i+1, i), 
                        findMaxSum(arr, i+1, j));
    }
    // if current element <= last chosen element
    // we can't chose current element
    else return findMaxSum(arr, i+1, j);
}

static int maxSumIS(int[] arr) {
    int n = arr.Length;
    return findMaxSum(arr, 1, 0);
}

//Driver Code Starts

public static void Main() {
    int[] arr = {1, 101, 2, 3, 100};
    Console.WriteLine(maxSumIS(arr));
}

}

//Driver Code Ends

JavaScript

function findMaxSum(arr, i, j) { if (i === arr.length + 1) return 0;

// i - current index(1-based index)
// j - last chosen index(1-based index)
// if j = 0, no element is chosen
if (j === 0 || arr[i - 1] > arr[j - 1]) {
    return Math.max(arr[i - 1] + findMaxSum(arr, i + 1, i),
                    findMaxSum(arr, i + 1, j)
    );
}
// if current element <= last chosen element
// we can't chose current element
else return findMaxSum(arr, i + 1, j);

}

function maxSumIS(arr) { let n = arr.length; return findMaxSum(arr, 1, 0); }

//Driver Code Starts // Driver code let arr = [1, 101, 2, 3, 100]; console.log(maxSumIS(arr));

//Driver Code Ends

`

[Better Approach 1] Using Top-Down DP (Memoization) - O(n2) Time and O(n2) Space

In the approach, we observe that many subproblems repeat. For example, while finding the maximum sum of an increasing subsequence starting at index i with the last chosen index j, we repeatedly compute results for the same (i, j) for several states (including or excluding i) across different recursive calls. To avoid recalculating these overlapping subproblems, we store the results of solved states in a DP table and reuse them whenever needed.

C++ `

//Driver Code Starts #include #include using namespace std; //Driver Code Ends

int findMaxSum(vector& arr, int i, int j, vector<vector>& dp) { if(i == arr.size() + 1) return 0;

// if this subproblem is already solved
// return the stored value
if(dp[i][j] != -1) return dp[i][j];

// i - current index(1-based index)
// j - last chosen index(1-based index)
// if j = 0, no element is chosen
if(j == 0 || arr[i-1] > arr[j-1]) {
    return dp[i][j] = max(arr[i-1] + findMaxSum(arr, i+1, i, dp),
                          findMaxSum(arr, i+1, j, dp));
}
// if current element <= last chosen element
// we can't chose current element
else return dp[i][j] = findMaxSum(arr, i+1, j, dp);

}

int maxSumIS(vector& arr) { int n = arr.size(); vector<vector> dp(n+1, vector(n+1, -1));

return findMaxSum(arr, 1, 0, dp);

}

//Driver Code Starts

int main() { vector arr = {1, 101, 2, 3, 100}; cout << maxSumIS(arr); } //Driver Code Ends

Java

//Driver Code Starts import java.util.Arrays;

class GFG { //Driver Code Ends

static int findMaxSum(int[] arr, int i, int j, int[][] dp) {
    if(i == arr.length + 1) return 0;
    
    // if this subproblem is already solved
    // return the stored value
    if(dp[i][j] != -1 ) return dp[i][j];
    
    // i - current index(1-based index)
    // j - last chosen index(1-based index)
    // if j = 0, no element is chosen
    if(j == 0 || arr[i-1] > arr[j-1]) {
        return dp[i][j] = Math.max(arr[i-1] + findMaxSum(arr, i+1, i, dp), 
                                   findMaxSum(arr, i+1, j, dp));
    }
    // if current element <= last chosen element
    // we can't chose current element
    else return dp[i][j] = findMaxSum(arr, i+1, j, dp);
}

static int maxSumIS(int arr[]) {
    int n = arr.length;
    int[][] dp = new int[n+1][n+1];
    
    // initialize dp array with invalid value
    for( int[] row : dp ) {
        Arrays.fill(row, -1);
    }
    return findMaxSum(arr, 1, 0, dp);
}

//Driver Code Starts

public static void main(String[] args) {
    int[] arr = {1, 101, 2, 3, 100};
    
    System.out.println(maxSumIS(arr));
}

}

//Driver Code Ends

Python

def findMaxSum(arr, i, j, dp): if i == len(arr) + 1: return 0

# if this subproblem is already solved
# return the stored value
if dp[i][j] != -1:
    return dp[i][j]

# i - current index(1-based index)
# j - last chosen index(1-based index)
# if j = 0, no element is chosen
if j == 0 or arr[i - 1] > arr[j - 1]:
    dp[i][j] = max(
        arr[i - 1] + findMaxSum(arr, i + 1, i, dp),
        findMaxSum(arr, i + 1, j, dp)
    )
    return dp[i][j]
# if current element <= last chosen element
# we can't chose current element
else:
    dp[i][j] = findMaxSum(arr, i + 1, j, dp)
    return dp[i][j]

def maxSumIS(arr): n = len(arr) dp = [[-1 for _ in range(n + 1)] for _ in range(n + 1)]

# initialize dp array with invalid value
for row in dp:
    for j in range(len(row)):
        row[j] = -1

return findMaxSum(arr, 1, 0, dp)

#Driver Code Starts if name == "main": arr = [1, 101, 2, 3, 100] print(maxSumIS(arr)) #Driver Code Ends

C#

//Driver Code Starts using System;

class GFG { //Driver Code Ends

static int findMaxSum(int[] arr, int i, int j, int[,] dp) {
    if(i == arr.Length + 1) return 0;
    
    // if this subproblem is already solved
    // return the stored value
    if(dp[i,j] != -1) return dp[i,j];
    
    // i - current index(1-based index)
    // j - last chosen index(1-based index)
    // if j = 0, no element is chosen
    if(j == 0 || arr[i-1] > arr[j-1]) {
        return dp[i,j] = Math.Max(arr[i-1] + findMaxSum(arr, i+1, i, dp), 
                                  findMaxSum(arr, i+1, j, dp));
    }
    // if current element <= last chosen element
    // we can't chose current element
    else return dp[i,j] = findMaxSum(arr, i+1, j, dp);
}

static int maxSumIS(int[] arr) {
    int n = arr.Length;
    int[,] dp = new int[n+1, n+1];
    
    // initialize dp array with invalid value
    for(int i = 0; i <= n; i++) {
        for(int j = 0; j <= n; j++) {
            dp[i,j] = -1;
        }
    }
    return findMaxSum(arr, 1, 0, dp);
}

//Driver Code Starts

public static void Main() {
    int[] arr = {1, 101, 2, 3, 100};
    Console.WriteLine(maxSumIS(arr));
}

} //Driver Code Ends

JavaScript

function findMaxSum(arr, i, j, dp) { if (i === arr.length + 1) return 0;

// if this subproblem is already solved
// return the stored value
if (dp[i][j] !== -1) return dp[i][j];

// i - current index(1-based index)
// j - last chosen index(1-based index)
// if j = 0, no element is chosen
if (j === 0 || arr[i - 1] > arr[j - 1]) {
    return dp[i][j] = Math.max(
        arr[i - 1] + findMaxSum(arr, i + 1, i, dp),
        findMaxSum(arr, i + 1, j, dp)
    );
}
// if current element <= last chosen element
// we can't chose current element
else return dp[i][j] = findMaxSum(arr, i + 1, j, dp);

}

function maxSumIS(arr) { let n = arr.length; let dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(-1));

// initialize dp array with invalid value
for (let i = 0; i <= n; i++) {
    dp[i].fill(-1);
}

return findMaxSum(arr, 1, 0, dp);

}

//Driver Code Starts // Driver code let arr = [1, 101, 2, 3, 100]; console.log(maxSumIS(arr)); //Driver Code Ends

`

[Better Approach 2] Using Bottom-Up DP (Tabulation) - O(n2) Time and O(n) Space

The main idea is to find the maximum sum increasing subsequence ending at each index i.
To do this, we look at all previous indices j such that arr[j] < arr[i]. This condition ensures that adding arr[i] at the end will keep the subsequence increasing. Among all such valid j, we find the one that gives the maximum sum subsequence ending at j, and then add arr[i] to extend it.
After calculating this for every index, we take the maximum value among all these subsequences — that’s our final answer.

C++ `

//Driver Code Starts #include <bits/stdc++.h> using namespace std;

//Driver Code Ends

int maxSumIS(vector& arr) { int n = arr.size(); vector dp(n); dp[0] = arr[0]; int ans = 0; for (int i = 0; i < n; i++) { // maximum sum of increasing // subsequence ending at a value < a[i] int maxSum = 0; for (int j = 0; j < i; j++) {

        // taking max among all smaller indices
        if (arr[j] < arr[i]) maxSum = max(maxSum, dp[j]);
    }

    // adding current element to 
    // the subsequence with max sum
    dp[i] = maxSum + arr[i];
    ans = max(dp[i], ans);
}
return ans;

}

//Driver Code Starts

int main() { vector arr = {1, 101, 2, 3, 100}; cout << maxSumIS(arr); }

//Driver Code Ends

Java

//Driver Code Starts import java.util.Arrays;

class GFG { //Driver Code Ends

static int maxSumIS(int[] arr) {
    int n = arr.length;
    int[] dp = new int[n];
    dp[0] = arr[0];
    int ans = 0;
    for( int i = 0; i < n ; i++ ) {
        // maximum sum of increasing 
        // subsequence ending at a value < a[i]
        int maxSum = 0;
        for( int j = 0; j < i; j++ ) {
            
            // taking max among all smaller indices
            if( arr[j] < arr[i] ) maxSum = Math.max(maxSum, dp[j]);
        }
        
        // adding current element to 
        // the subsequence with max sum
        dp[i] = maxSum+arr[i];
        ans = Math.max(dp[i], ans);
    }
    return ans;
}

//Driver Code Starts

public static void main(String[] args) {
    int[] arr = {1, 101, 2, 3, 100};
    System.out.println(maxSumIS(arr));
}

}

//Driver Code Ends

Python

def maxSumIS(arr): n = len(arr) dp = [0] * n dp[0] = arr[0] ans = 0 for i in range(n): # maximum sum of increasing # subsequence ending at a value < a[i] maxSum = 0 for j in range(i):

        # taking max among all smaller indices
        if arr[j] < arr[i]:
            maxSum = max(maxSum, dp[j])
    
    # adding current element to 
    # the subsequence with max sum
    dp[i] = maxSum + arr[i]
    ans = max(dp[i], ans)
return ans

#Driver Code Starts if name == "main": arr = [1, 101, 2, 3, 100] print(maxSumIS(arr))

#Driver Code Ends

C#

//Driver Code Starts using System;

class GFG { //Driver Code Ends

static int MaxSumIS(int[] arr) {
    int n = arr.Length;
    int[] dp = new int[n];
    dp[0] = arr[0];
    int ans = 0;
    for (int i = 0; i < n; i++) {
        // maximum sum of increasing 
        // subsequence ending at a value < a[i]
        int maxSum = 0;
        for (int j = 0; j < i; j++) {

            // taking max among all smaller indices
            if (arr[j] < arr[i]) maxSum = Math.Max(maxSum, dp[j]);
        }

        // adding current element to 
        // the subsequence with max sum
        dp[i] = maxSum + arr[i];
        ans = Math.Max(dp[i], ans);
    }
    return ans;
}

//Driver Code Starts

public static void Main() {
    int[] arr = {1, 101, 2, 3, 100};
    Console.WriteLine(MaxSumIS(arr));
}

} //Driver Code Ends

JavaScript

function maxSumIS(arr) { const n = arr.length; const dp = new Array(n).fill(0); dp[0] = arr[0]; let ans = 0;

for (let i = 0; i < n; i++) {
    // maximum sum of increasing 
    // subsequence ending at a value < a[i]
    let maxSum = 0;
    for (let j = 0; j < i; j++) {

        // taking max among all smaller indices
        if (arr[j] < arr[i]) maxSum = Math.max(maxSum, dp[j]);
    }

    // adding current element to 
    // the subsequence with max sum
    dp[i] = maxSum + arr[i];
    ans = Math.max(dp[i], ans);
}
return ans;

}

//Driver Code Starts // Driver code const arr = [1, 101, 2, 3, 100]; console.log(maxSumIS(arr));

//Driver Code Ends

`

[Expected Approach] Using Optimized Dynamic Programming - O(n log(n)) time and O(n) space

In the above approach, for every element, we look back through all previous elements to find the one with the highest subsequence sum among smaller values. We can observe that it can be optimized if we store-computed results in an ordered data structure that keeps elements sorted by their values. This allows us to quickly find the best result among all smaller elements using a logarithmic lookup, instead of checking each one individually.

C++ `

//Driver Code Starts #include #include using namespace std;

//Driver Code Ends

int maxSumIS(vector& arr) {

// key = value in arr, value = max sum ending 
// with that value or less than that value
map<int, int> dp; 
int ans = 0;

for (int val : arr) {
    
    // Find the best sum among 
    // all elements smaller than val
    auto it = dp.lower_bound(val);
    int bestSmaller = 0;
    if (it != dp.begin()) {
        --it;
        bestSmaller = it->second;
    }

    int currSum = bestSmaller + val;

    // If this value gives a 
    // better sum, update TreeMap
    if (dp[val] < currSum) {
        dp[val] = currSum;

        // Remove entries with greater 
        // keys with smaller or equal sums
        auto higher = dp.upper_bound(val);
        while (higher != dp.end() && higher->second <= currSum) {
            higher = dp.erase(higher);
        }
    }

    ans = max(ans, currSum);
}

return ans;

}

//Driver Code Starts

int main() { vector arr = {1, 101, 2, 3, 100}; cout << maxSumIS(arr); } //Driver Code Ends

Java

//Driver Code Starts import java.util.TreeMap; import java.util.Map;

class GFG { //Driver Code Ends

static int maxSumIS(int[] arr) {
    
    // key = value in arr, value = max sum ending 
    // with that value or less than that value
    TreeMap<Integer, Integer> dp = new TreeMap<>(); 
    int ans = 0;

    for (int val : arr) {
        
        // Find the best sum among 
        // all elements smaller than val
        Map.Entry<Integer, Integer> entry = dp.lowerEntry(val);
        int bestSmaller = (entry == null) ? 0 : entry.getValue();

        int currSum = bestSmaller + val;

        // If this value gives a 
        // better sum, update TreeMap
        if (dp.getOrDefault(val, 0) < currSum) {
            dp.put(val, currSum);

            // Remove entries with greater 
            // keys with smaller or equal sums
            Integer higher = dp.higherKey(val);
            while (higher != null && dp.get(higher) <= currSum) {
                dp.remove(higher);
                higher = dp.higherKey(val);
            }
        }

        ans = Math.max(ans, currSum);
    }

    return ans;
}

//Driver Code Starts

public static void main(String[] args) {
    int[] arr = {1, 101, 2, 3, 100};
    System.out.println(maxSumIS(arr)); 
}

}

//Driver Code Ends

Python

#Driver Code Starts from bisect import bisect_left, insort

#Driver Code Ends

def maxSumIS(arr):

# key = value in arr, value = max sum ending 
# with that value or less than that value
dp = {}
keys = []
ans = 0

for val in arr:
    
    # Find the best sum among 
    # all elements smaller than val
    idx = bisect_left(keys, val)
    bestSmaller = 0
    if idx > 0:
        bestSmaller = dp[keys[idx - 1]]

    currSum = bestSmaller + val

    # If this value gives a 
    # better sum, update TreeMap
    if val not in dp or dp[val] < currSum:
        dp[val] = currSum
        if val not in keys:
            insort(keys, val)

        # Remove entries with greater 
        # keys with smaller or equal sums
        i = bisect_left(keys, val) + 1
        while i < len(keys):
            if dp[keys[i]] <= currSum:
                del dp[keys[i]]
                keys.pop(i)
            else:
                break

    ans = max(ans, currSum)

return ans

#Driver Code Starts

if name == "main": arr = [1, 101, 2, 3, 100] print(maxSumIS(arr))

#Driver Code Ends

C#

//Driver Code Starts using System; using System.Collections.Generic;

class GFG { //Driver Code Ends

static int MaxSumIS(int[] arr) {
    
    // key = value in arr, value = max sum ending 
    // with that value or less than that value
    SortedDictionary<int, int> dp = new SortedDictionary<int, int>(); 
    int ans = 0;

    foreach (int val in arr) {
        
        // Find the best sum among 
        // all elements smaller than val
        int bestSmaller = 0;
        foreach (var kv in dp) {
            if (kv.Key < val) bestSmaller = kv.Value;
            else break;
        }

        int currSum = bestSmaller + val;

        // If this value gives a 
        // better sum, update TreeMap
        if (!dp.ContainsKey(val) || dp[val] < currSum) {
            dp[val] = currSum;

            // Remove entries with greater 
            // keys with smaller or equal sums
            var keysToRemove = new List<int>();
            foreach (var kv in dp) {
                if (kv.Key > val && kv.Value <= currSum)
                    keysToRemove.Add(kv.Key);
            }
            foreach (var k in keysToRemove)
                dp.Remove(k);
        }

        ans = Math.Max(ans, currSum);
    }

    return ans;
}

//Driver Code Starts

public static void Main() {
    int[] arr = {1, 101, 2, 3, 100};
    Console.WriteLine(MaxSumIS(arr));
}

} //Driver Code Ends

JavaScript

function maxSumIS(arr) {

// key = value in arr, value = max sum ending 
// with that value or less than that value
const dp = new Map(); 
const sortedKeys = [];
let ans = 0;

for (const val of arr) {
    
    // Find the best sum among 
    // all elements smaller than val
    let bestSmaller = 0;
    for (const k of sortedKeys) {
        if (k < val) bestSmaller = Math.max(bestSmaller, dp.get(k));
        else break;
    }

    const currSum = bestSmaller + val;

    // If this value gives a 
    // better sum, update Map
    if (!dp.has(val) || dp.get(val) < currSum) {
        dp.set(val, currSum);
        if (!sortedKeys.includes(val)) {
            sortedKeys.push(val);
            sortedKeys.sort((a, b) => a - b);
        }

        // Remove entries with greater 
        // keys with smaller or equal sums
        for (let i = sortedKeys.length - 1; i >= 0; i--) {
            const higher = sortedKeys[i];
            if (higher > val && dp.get(higher) <= currSum) {
                dp.delete(higher);
                sortedKeys.splice(i, 1);
            }
        }
    }

    ans = Math.max(ans, currSum);
}

return ans;

}

//Driver Code Starts // Driver code const arr = [1, 101, 2, 3, 100]; console.log(maxSumIS(arr));

//Driver Code Ends

`