Open In App

Maximum sum subsequence of any size which is decreasing-increasing alternatively

Improve
Improve
Like Article
Like
Save
Share
Report

Given an array of integers arr[], find the subsequence with maximum sum whose elements are first decreasing, then increasing, or vice versa, The subsequence can start anywhere in the main sequence, not necessarily at the first element of the main sequence.

A sequence {x1, x2, .. xn} is an alternating sequence if its elements satisfy one of the following relations (As described in the article Longest alternating subsequence)

Two adjacent elements that are equal are not counted as alternating.

x1 < x2 > x3 < x4 > x5 < …. xn 
or 
x1 > x2 < x3 > x4 < x5 > …. xn

 Examples :

Input: arr[] = {4, 8, 2, 5, 6, 8}
Output: 26
Explanation: Alternating subsequence with maximum sum is {4, 8, 6, 8}. Sum = 4 + 8 + 6 + 8 = 26

Input: arr[] = {0, 1, 1, 1, 3, 2, 5}
Output: 11
Explanation: Alternating subsequence with maximum sum is {1, 3, 2, 5}. Sum = 1 + 3 + 2 + 5 = 11

Input: arr[] = {1, 4, 5}
Output: 9
Explanation: Alternating subsequence with maximum sum is {4, 5}. Sum = 4 + 5 = 9

Input: arr[] = {1, 0, 1, 0, 0, 3}
Output: 5
Explanation: Alternating subsequence with maximum sum is {1, 0, 1, 0, 3}. Sum = 1 + 0 + 1 + 0 + 3 = 5

Input: arr[] = {5, 5}
Output: 5
Explanation: Alternating subsequence with maximum sum is {5}. Sum = 5

 

 

This problem is an extension of the Maximum sum alternating subsequence problem. Unlike the previous problem, now we need to calculate the maximum sum of the alternating subsequence regardless of where it is in the main sequence, and regardless of whether it starts with two elements ascending or two descending elements.

Approach: This problem can be solved by Dynamic Programming combined with Backtracking. Suppose, we have an array arr[] with any N elements. 

  • We can consider each arr[i] element as a terminating element of an alternating sequence.
  • There can be many alternating subsequences whose last element is arr[i], but surely one of them is the sequence with the greatest sum.
  • Furthermore, the maximum sum of the alternating subsequences ending with arr[i] is not necessarily greater than the maximum sum of the alternating subsequences ending with arr[i-1]. And this is the basis for implementing the algorithm according to the Backtracking technique.
  • Have an array say maxSum[] of length N which will store all the maximum sums ending at each element in the array arr[]. Specifically, maxSum[i] will store the maximum sum of the alternating subsequence ending at the value arr[i].
  • We will use another array before[] to store the value preceding the last element of each alternating subsequence with maximum sum.

For example, an array arr[] = {1, 5, 7, 3, 4, 5} will have an alternating subsequence {5, 7, 3, 4} whose maximum sum is 5 + 7 + 3 + 4 = 19. 
The subsequence {5, 7, 3, 4} ends at the value 4, which has index 4 in the array arr[]. 
So we have arr[4] = 4, maxSum[4] = 19,  before[4] = 3 (because arr[3] = 3). 
Those values ​​will be calculated and saved into two arrays maxSum[] and before[] during the calculation and can be reused as needed.

Use the following steps to implement the approach:

  • Use a loop to iterate through every element of the array arr[]. Each time we traverse an arr[i] element, we look at the elements that precede it:

i Loop traversal from 1 to N-1 (There’s no need to start at position 0, because that’s the base case):
        j Loop traversal from 0 to i-1:

  • At each state i and j, we will reuse any maxSum[j] with 0 <= j < i according to the principle of Dynamic Programming:

if:
        (arr[i] > arr[j] && arr[before[j]] > arr[j]) 
                            or
        (arr[i] < arr[j] && arr[before[j]] < arr[j])

then:
        sum = arr[i] + maxSum[j] 

  • If maxSum[j] does not satisfy the above condition, it can be two equal elements. Since two equal elements are not treated as an alternating sequence, we only take one of them.

if: 
        arr[i] == arr[j]

then:
        sum = maxSum[j]

  • Or it may not be any of the above, like having more than two elements in increasing or having more than two elements in decreasing order.

Example {1, 5, 7, 3, 4, 5}. Suppose we are at i = 5, j = 4

  • Then before[4] is the 3-rd element, and (arr[before[j]], arr[j], arr[i]) = (arr[before[4]], arr[4], arr[5]) = (arr[3], arr[4], arr[5]) = (3, 4, 5).
  • The above one is not a valid alternating subsequence, meaning that before[j] is not the index of a valid value.
  • We will look at the preceding element of arr[before[j]] in its alternating sequence: before[before[j]] = before[3] = 2, and arr[2] with arr[4] and arr[5] form a valid subsequence (7, 4, 5).
  • So we get a value of arr[5] + arr[4] + maxSum[2], which is the final sum of state {i = 5, j = 4}.
  • We need to compare it with other {i and j} states (like {i = 5, j = 3}, {i = 5, j = 2}…) to get the final result for maxSum[5].
  • Then save j into the before[5] if it precedes arr[5] and helps arr[5] form a valid subsequence with a maximum sum at state 5.

Take a look at the illustration below to get the idea clear.

index      :   0  1  2  3  4  5  || N = 6
arr[]      :   1, 5, 7, 3, 4, 5

—————————————————————————————————-

maxSum[0]  :    0  0  0  0  0   = 1 base case
before[-1] :  -1  _  _  _  _  _

maxSum[1]  :   1   0  0  0  0   = 6 because 1 + 5
before[1]  :  -1   _  _  _  _

maxSum[2]  :   1  6 12  0  0  0   = 12 because 1, 5, 7 isn’t alternating subsequence, and just 5 & 7 
before[2]  :  -1  0  1  _  _  _    is valid, we use backtracking at index 2 so 
we will go through the following subsequence: {1, 7}, {5, 7}. We have {5, 7} because before[1] is index 0, 
we continue to find value at before[before[1]] = -1, whose value is 0, so 0 + 5 + 7 = 12 > 1 + 7.

maxSum[3]  :   1  6 12 15  0  0   = 15 because 5, 7, 3 is valid alternating subsequence, 
before[3]  :  -1  0  1  2  _  _ so 3 + max(maxSum[2], maxSum[1], maxSum[0]) = 3 + 12.

maxSum[4]  :   1  6 12 15 19  0  = 19 because 5, 7, 3, 4 is valid alternating subsequence,
before[4]  :  -1  0  1  2  3  _   so 4 + max(maxSum[3], maxSum[2],… maxSum[0]) = 4 + 15.

maxSum[5]  :   1  6 12 15 19 21   = 21, arr[5] cannot be the next element of maxSum[4] because 3, 4, 5
before[5]  :  -1  0  1  2  3  2 isn’t alternating subsequence. So we need use backtracking here. 
We will treat the value 3 as an invalid value for the alternating subsequence ending in arr[5]. 
We need to find the preceding element of before[arr[4]] in the sum of maxSum[4] recursively, 
that is index 2. Now 7, 4, 5 have formed a valid alternating subsequence. 
So we have 5 + max(backtracking(index 4), maxSum[3], maxSum[2],…) = 5 + 16 ( because 4 + 7 + 5)
Then, final max sum of alternating subsequence of arr is max element of “maxSum” array.

Output: 21

Below is the implementation of the above approach:

C++




// C++ code to implement the above approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Function for backtracking
int backtracking(int arr[], int maxSum[], int before[],
                 int N, int root, int bef_root,
                 int bbef_root)
{
    // {root, bef_root} represents state{i, j}
    // bbef_root represent before[before[j]]
    // We ignore the invalid before[j] index
 
    // Base case:
    if (bbef_root == -1)
        return arr[bef_root];
 
    // The case of a subsequence with
    // alternating parts:
    if ((arr[root] > arr[bef_root]
         && arr[bbef_root] > arr[bef_root])
        || (arr[root] < arr[bef_root]
            && arr[bbef_root] < arr[bef_root])) {
        return arr[bef_root] + maxSum[bbef_root];
    }
 
    // case (arr[bef_root] == arr[bbef_root])
    else {
        return backtracking(arr, maxSum, before, N, root,
                            bef_root, before[bbef_root]);
    }
}
 
int maxSumAlternatingSubsequence(int arr[], int N)
{
 
    // Max alternating subsequence sum
    // ending at arr[i].
    int maxSum[N];
 
    // Array to store the index of the element
    // preceding the last element at maxSum[i]
    int before[N];
 
    // Value initialization for arrays:
    fill_n(&maxSum[0], N, 0);
    maxSum[0] = arr[0];
    before[0] = -1;
 
    // Iterate over the array:
    for (int i = 1; i < N; i++)
        for (int j = 0; j < i; j++) {
            int currentMax = 0;
            if ((arr[i] > arr[j]
                 && arr[before[j]] > arr[j])
                || (arr[i] < arr[j]
                    && arr[before[j]] < arr[j])
                || before[j] == -1) {
 
                // Whenever an element is
                // between two smaller elements
                //  or between two larger elements,
                //  it is an alternating sequence.
                //  When the preceding index of j is -1,
                //  we need to treat it explicitly,
                // because -1 is not a valid index.
                currentMax = (arr[i] == arr[j])
                                 ? maxSum[j]
                                 : arr[i] + maxSum[j];
            }
           
            else if (arr[i] == arr[j]) {
                // If arr[i] is equal to arr[j] then
                // only take it once,
                // before[j] cannot be equal to -1.
                currentMax = maxSum[j];
            }
           
            else {
                // Perform backtracking
                // If three adjacent elements
                // are increasing or decreasing.
                currentMax = arr[i]
                             + backtracking(
                                 arr, maxSum, before, N, i,
                                 j, before[before[j]]);
            }
 
            if (currentMax >= maxSum[i]) {
                // Stores the maximum sum and
                // the index preceding
                // the last element
                // at position i of
                // current alternating subsequence
                // after each iteration.
                maxSum[i] = currentMax;
                before[i] = j;
            }
        }
 
    // get max result in array
    return *max_element(maxSum, maxSum + N);
}
 
// Driver code
int main()
{
    int arr[] = { 1, 5, 7, 3, 4, 5 };
    int N = sizeof(arr) / sizeof(int);
 
    // Maximum sum of alternating subsequence
    // of array arr[]
    cout << maxSumAlternatingSubsequence(arr, N)
      << endl;
 
    return 0;
}


Java




// Java code to implement the above approach
import java.io.*;
 
class GFG{
 
// Function for backtracking
static int backtracking(int arr[], int maxSum[],
                        int before[], int N, int root,
                        int bef_root, int bbef_root)
{
     
    // {root, bef_root} represents state{i, j}
    // bbef_root represent before[before[j]]
    // We ignore the invalid before[j] index
 
    // Base case:
    if (bbef_root == -1)
        return arr[bef_root];
 
    // The case of a subsequence with
    // alternating parts:
    if ((arr[root] > arr[bef_root] &&
    arr[bbef_root] > arr[bef_root]) ||
        (arr[root] < arr[bef_root] &&
    arr[bbef_root] < arr[bef_root]))
    {
        return arr[bef_root] + maxSum[bbef_root];
    }
 
    // case (arr[bef_root] == arr[bbef_root])
    else
    {
        return backtracking(arr, maxSum, before, N,
                            root, bef_root,
                            before[bbef_root]);
    }
}
 
static int maxSumAlternatingSubsequence(int arr[],
                                        int N)
{
 
    // Max alternating subsequence sum
    // ending at arr[i].
    int maxSum[] = new int[N];
 
    // Array to store the index of the element
    // preceding the last element at maxSum[i]
    int before[] = new int[N];
 
    // Value initialization for arrays:
    maxSum[0] = arr[0];
    before[0] = -1;
 
    // Iterate over the array:
    for(int i = 1; i < N; i++)
        for(int j = 0; j < i; j++)
        {
            int currentMax = 0;
            if ((arr[i] > arr[j] && before[j] != -1 &&
                   arr[before[j]] > arr[j]) ||
                          (arr[i] < arr[j] && before[j] != -1 &&
                   arr[before[j]] < arr[j]) || before[j] == -1)
            {
                 
                // Whenever an element is
                // between two smaller elements
                // or between two larger elements,
                // it is an alternating sequence.
                // When the preceding index of j is -1,
                // we need to treat it explicitly,
                // because -1 is not a valid index.
                currentMax = (arr[i] == arr[j]) ? maxSum[j] :
                              arr[i] + maxSum[j];
            }
 
            else if (arr[i] == arr[j])
            {
                 
                // If arr[i] is equal to arr[j] then
                // only take it once,
                // before[j] cannot be equal to -1.
                currentMax = maxSum[j];
            }
            else
            {
                 
                // Perform backtracking
                // If three adjacent elements
                // are increasing or decreasing.
                currentMax = arr[i] + backtracking(arr, maxSum,
                                                   before, N, i, j,
                                                   before[before[j]]);
            }
 
            if (currentMax >= maxSum[i])
            {
                 
                // Stores the maximum sum and the index
                // preceding the last element at position
                // i of current alternating subsequence
                // after each iteration.
                maxSum[i] = currentMax;
                before[i] = j;
            }
        }
 
    // get max result in array
    int maxi = 0;
 
    for(int i = 0; i < N; i++)
    {
        maxi = Math.max(maxSum[i], maxi);
    }
    return maxi;
}
 
// Driver code
public static void main(String[] args)
{
    int arr[] = { 1, 5, 7, 3, 4, 5 };
    int N = arr.length;
 
    // Maximum sum of alternating subsequence
    // of array arr[]
    System.out.println(
        maxSumAlternatingSubsequence(arr, N));
}
}
 
// This code is contributed by Potta Lokesh


Python3




# Python code to implement the above approach
 
# Function for backtracking
def backtracking(arr, maxSum, before, N, root, bef_root, bbef_root):
 
    # {root, bef_root} represents state{i, j}
    # bbef_root represent before[before[j]]
    # We ignore the invalid before[j] index
 
    # Base case:
    if (bbef_root == -1):
        return arr[bef_root]
 
    # The case of a subsequence with
    # alternating parts:
    if (arr[root] > arr[bef_root] and arr[bbef_root] > arr[bef_root]) or (arr[root] < arr[bef_root] and arr[bbef_root] < arr[bef_root]):
        return arr[bef_root] + maxSum[bbef_root]
 
    # case (arr[bef_root] == arr[bbef_root])
    else:
        return backtracking(arr, maxSum, before, N,
                            root, bef_root,
                            before[bbef_root])
 
def maxSumAlternatingSubsequence(arr, N):
 
    # Max alternating subsequence sum
    # ending at arr[i].
    maxSum = [0] * N
 
    # Array to store the index of the element
    # preceding the last element at maxSum[i]
    before = [0] * N
 
    # Value initialization for arrays:
    maxSum[0] = arr[0]
    before[0] = -1
 
    # Iterate over the array:
    for i in range(N):
        for j in range(i):
            currentMax = 0
            if (arr[i] > arr[j] and before[j] != -1 and arr[before[j]] > arr[j]) or (arr[i] < arr[j] and before[j] != -1 and arr[before[j]] < arr[j]) or before[j] == -1:
 
                # Whenever an element is
                # between two smaller elements
                # or between two larger elements,
                # it is an alternating sequence.
                # When the preceding index of j is -1,
                # we need to treat it explicitly,
                # because -1 is not a valid index.
                currentMax = maxSum[j] if (
                    arr[i] == arr[j]) else arr[i] + maxSum[j]
 
            elif (arr[i] == arr[j]):
 
                # If arr[i] is equal to arr[j] then
                # only take it once,
                # before[j] cannot be equal to -1.
                currentMax = maxSum[j]
            else:
 
                # Perform backtracking
                # If three adjacent elements
                # are increasing or decreasing.
                currentMax = arr[i] + backtracking(arr, maxSum,
                                                   before, N, i, j,
                                                   before[before[j]])
 
            if (currentMax >= maxSum[i]):
 
                # Stores the maximum sum and the index
                # preceding the last element at position
                # i of current alternating subsequence
                # after each iteration.
                maxSum[i] = currentMax
                before[i] = j
 
    # get max result in array
    maxi = 0
 
    for i in range(N):
        maxi = max(maxSum[i], maxi)
    return maxi
 
# Driver code
arr = [1, 5, 7, 3, 4, 5]
N = len(arr)
 
# Maximum sum of alternating subsequence
# of array arr
print(maxSumAlternatingSubsequence(arr, N))
 
# This code is contributed by gfgking


C#




// C# program for above approach
using System;
class GFG{
 
// Function for backtracking
static int backtracking(int []arr, int []maxSum,
                        int []before, int N, int root,
                        int bef_root, int bbef_root)
{
     
    // {root, bef_root} represents state{i, j}
    // bbef_root represent before[before[j]]
    // We ignore the invalid before[j] index
 
    // Base case:
    if (bbef_root == -1)
        return arr[bef_root];
 
    // The case of a subsequence with
    // alternating parts:
    if ((arr[root] > arr[bef_root] &&
    arr[bbef_root] > arr[bef_root]) ||
        (arr[root] < arr[bef_root] &&
    arr[bbef_root] < arr[bef_root]))
    {
        return arr[bef_root] + maxSum[bbef_root];
    }
 
    // case (arr[bef_root] == arr[bbef_root])
    else
    {
        return backtracking(arr, maxSum, before, N,
                            root, bef_root,
                            before[bbef_root]);
    }
}
 
static int maxSumAlternatingSubsequence(int []arr,
                                        int N)
{
 
    // Max alternating subsequence sum
    // ending at arr[i].
    int []maxSum = new int[N];
 
    // Array to store the index of the element
    // preceding the last element at maxSum[i]
    int []before = new int[N];
 
    // Value initialization for arrays:
    maxSum[0] = arr[0];
    before[0] = -1;
 
    // Iterate over the array:
    for(int i = 1; i < N; i++)
        for(int j = 0; j < i; j++)
        {
            int currentMax = 0;
            if ((arr[i] > arr[j] && before[j] != -1 &&
                   arr[before[j]] > arr[j]) ||
                          (arr[i] < arr[j] && before[j] != -1 &&
                   arr[before[j]] < arr[j]) || before[j] == -1)
            {
                 
                // Whenever an element is
                // between two smaller elements
                // or between two larger elements,
                // it is an alternating sequence.
                // When the preceding index of j is -1,
                // we need to treat it explicitly,
                // because -1 is not a valid index.
                currentMax = (arr[i] == arr[j]) ? maxSum[j] :
                              arr[i] + maxSum[j];
            }
 
            else if (arr[i] == arr[j])
            {
                 
                // If arr[i] is equal to arr[j] then
                // only take it once,
                // before[j] cannot be equal to -1.
                currentMax = maxSum[j];
            }
            else
            {
                 
                // Perform backtracking
                // If three adjacent elements
                // are increasing or decreasing.
                currentMax = arr[i] + backtracking(arr, maxSum,
                                                   before, N, i, j,
                                                   before[before[j]]);
            }
 
            if (currentMax >= maxSum[i])
            {
                 
                // Stores the maximum sum and the index
                // preceding the last element at position
                // i of current alternating subsequence
                // after each iteration.
                maxSum[i] = currentMax;
                before[i] = j;
            }
        }
 
    // get max result in array
    int maxi = 0;
 
    for(int i = 0; i < N; i++)
    {
        maxi = Math.Max(maxSum[i], maxi);
    }
    return maxi;
}
 
// Driver Code
public static void Main()
{
    int []arr = { 1, 5, 7, 3, 4, 5 };
    int N = arr.Length;
 
    // Maximum sum of alternating subsequence
    // of array arr[]
    Console.Write(
        maxSumAlternatingSubsequence(arr, N));
}
}
 
// This code is contributed by Samim Hossain Mondal.


Javascript




<script>
// javascript code to implement the above approach
 
// Function for backtracking
function backtracking(arr , maxSum, before , N , root, bef_root , bbef_root)
{
     
    // {root, bef_root} represents state{i, j}
    // bbef_root represent before[before[j]]
    // We ignore the invalid before[j] index
 
    // Base case:
    if (bbef_root == -1)
        return arr[bef_root];
 
    // The case of a subsequence with
    // alternating parts:
    if ((arr[root] > arr[bef_root] &&
    arr[bbef_root] > arr[bef_root]) ||
        (arr[root] < arr[bef_root] &&
    arr[bbef_root] < arr[bef_root]))
    {
        return arr[bef_root] + maxSum[bbef_root];
    }
 
    // case (arr[bef_root] == arr[bbef_root])
    else
    {
        return backtracking(arr, maxSum, before, N,
                            root, bef_root,
                            before[bbef_root]);
    }
}
 
function maxSumAlternatingSubsequence(arr,N)
{
 
    // Max alternating subsequence sum
    // ending at arr[i].
    var maxSum = Array.from({length: N}, (_, i) => 0);
 
    // Array to store the index of the element
    // preceding the last element at maxSum[i]
    var before = Array.from({length: N}, (_, i) => 0);
 
    // Value initialization for arrays:
    maxSum[0] = arr[0];
    before[0] = -1;
 
    // Iterate over the array:
    for(var i = 1; i < N; i++)
        for(var j = 0; j < i; j++)
        {
            var currentMax = 0;
            if ((arr[i] > arr[j] && before[j] != -1 &&
                   arr[before[j]] > arr[j]) ||
                          (arr[i] < arr[j] && before[j] != -1 &&
                   arr[before[j]] < arr[j]) || before[j] == -1)
            {
                 
                // Whenever an element is
                // between two smaller elements
                // or between two larger elements,
                // it is an alternating sequence.
                // When the preceding index of j is -1,
                // we need to treat it explicitly,
                // because -1 is not a valid index.
                currentMax = (arr[i] == arr[j]) ? maxSum[j] :
                              arr[i] + maxSum[j];
            }
 
            else if (arr[i] == arr[j])
            {
                 
                // If arr[i] is equal to arr[j] then
                // only take it once,
                // before[j] cannot be equal to -1.
                currentMax = maxSum[j];
            }
            else
            {
                 
                // Perform backtracking
                // If three adjacent elements
                // are increasing or decreasing.
                currentMax = arr[i] + backtracking(arr, maxSum,
                                                   before, N, i, j,
                                                   before[before[j]]);
            }
 
            if (currentMax >= maxSum[i])
            {
                 
                // Stores the maximum sum and the index
                // preceding the last element at position
                // i of current alternating subsequence
                // after each iteration.
                maxSum[i] = currentMax;
                before[i] = j;
            }
        }
 
    // get max result in array
    var maxi = 0;
 
    for(var i = 0; i < N; i++)
    {
        maxi = Math.max(maxSum[i], maxi);
    }
    return maxi;
}
 
// Driver code
var arr = [ 1, 5, 7, 3, 4, 5 ];
var N = arr.length;
 
// Maximum sum of alternating subsequence
// of array arr
document.write(
maxSumAlternatingSubsequence(arr, N));
 
// This code is contributed by 29AjayKumar
</script>


Output

21

Time Complexity: O(N2) where N is the length of the array.
Auxiliary Space: O(N)

 



Last Updated : 11 Jan, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads