Open In App

Maximize sum of given array after removing valleys

Last Updated : 22 Sep, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array arr[] of integers of size N, the task is to maximize the sum of the array after removing valleys from the array when only reducing the value of an element is allowed i.e. the new formed array should not contain any element which has greater value after modification.

Valleys:- An index j is considered as a valley point if  arr[i] > arr[j] and arr[ k ] > arr[ j ]  given that (i < j < k).

Examples

Input : arr[] = { 5, 10, 15 }
Output: 30 
Explanation: As array does not contain any valley, so there is no need to reduce any element.

Input : arr[] = { 8, 1, 10, 1, 8 }
Output : 14   
Explanation: new_arr=> [1, 1, 10, 1, 1]  and  sum = 14, new_arr can also be constructed as [8, 1, 1, 1, 1], but the sum will be less.

 

Naive Approach: Consider each element as a peak element and start in decreasing order in both directions of the peak element. 
 

If arr = [8, 1, 10, 1, 8 ] 
Consider 10 as peak element and then the final array would like [ 1, 1, 10, 1, 1]

Time Complexity: O(N2)
Auxiliary Space: O(1)

Efficient Approach: Instead of taking each index as a peak point. Calculate prefix and suffix sum array for each index(idx). Prefix array can be used to store the sum of heights from 0 . . . idx satisfying the condition that there is no valley on the left side and idx has the peak element. Suffix sum array also satisfy the same conditions for the suffix of the index idx

The element at an index can span in the left direction, till a smaller element is found, assuming that the current element is the highest one. The next smaller element using stack concept can be used here with minor changes.

Follow the steps mentioned below:

  • Build the prefix sum (left) and suffix sum (right) array. There will be two case while building the arrays. Consider the cases for prefix sum array below. Same is applicable for suffix sum array also.
    1. The current element is the smallest amongst all elements till now.
      So, left [ curr_idx ] = ( i +1 ) * arr[i]  . Here (i + 1) denotes the range .
    2. There is a smaller element with small_idx, on the left side.
      So, left [ curr_idx] = left[ small_idx ] + (current_idx – small_idx) *arr[i]
      Using same method  right sum array can be calculated .
  • Now, iterate through each index and calculate answer = left[idx] + right[idx] – arr[idx]

Note: arr[idx] is subtracted, because arr[ idx ] is added in left[] as well as right[] array.

See the illustration below for better understanding.

Illustration:

Consider the example  arr[] = { 5, 1, 8 } 
While building the prefix sum array 
At index = 0: There is no element in the left[0] = 5.
Index = 1: 1 is the smallest among all from the left. So, left[1] = 1 * 2 = 2. 
Index = 2: 8 has smaller element 1 at index 1. So left[2] = left[1] + 8*(2 – 1) = 2 + 8 = 10.
So left[] = {5, 2, 10}. Similarly right[] = {7, 2, 8}.
Now while traversing to calculate the answer the values from index 0, 1 and 2 are (5 + 7 – 5), (2 + 2 – 1), (10 + 8 – 8) respectively. The maximum among these is 10. So the answer is 10.

 Below is the implementation of the above approach:

C++




// C++ code for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Function to get the maximum cost
int solve(vector<int>& arr)
{
  int n = arr.size();
  vector<int> left(n, 0);
  vector<int> right(n, 0);
  vector<int> stack;
 
  // Calculate left array
  for (int i = 0; i < n; i++) {
    int curr = arr[i];
    while (stack.size() != 0
           && arr[stack[stack.size() - 1]] >= curr) {
      stack.pop_back();
    }
    // Case 1
    if (stack.size() == 0)
      left[i] = (i + 1) * (arr[i]);
 
    // Case 2
    else {
      int small_idx = stack[stack.size() - 1];
      left[i] = left[small_idx]
        + (i - small_idx) * (arr[i]);
    }
    stack.push_back(i);
  }
  stack.clear();
 
  // Calculate suffix sum array
  for (int i = n - 1; i > -1; i--) {
    int curr = arr[i];
    while (stack.size() != 0
           && arr[stack[stack.size() - 1]] >= curr) {
      stack.pop_back();
    }
 
    if (stack.size() == 0)
      right[i] = (n - i) * (arr[i]);
 
    else {
      int small_idx = stack[stack.size() - 1];
      right[i] = right[small_idx]
        + (small_idx - i) * (arr[i]);
    }
    stack.push_back(i);
  }
  int ans = 0;
 
  for (int i = 0; i < n; i++) {
    int curr = left[i] + right[i] - arr[i];
    ans = max(ans, curr);
  }
  return (ans);
}
 
// Driver code
int main()
{
  vector<int> arr = { 5, 1, 8 };
  cout << solve(arr);
 
  return 0;
}
 
// This code is contributed by rakeshsahni


Java




// Java code for the above approach
import java.util.*;
class GFG{
 
// Function to get the maximum cost
static int solve(int[] arr)
{
  int n = arr.length;
  int []left = new int[n];
  int []right = new int[n];
  Vector<Integer> stack = new Vector<Integer>();
 
  // Calculate left array
  for (int i = 0; i < n; i++) {
    int curr = arr[i];
    while (stack.size() != 0
           && arr[stack.get(stack.size() - 1)] >= curr) {
      stack.remove(stack.size() - 1);
    }
    // Case 1
    if (stack.size() == 0)
      left[i] = (i + 1) * (arr[i]);
 
    // Case 2
    else {
      int small_idx = stack.get(stack.size() - 1);
      left[i] = left[small_idx]
        + (i - small_idx) * (arr[i]);
    }
    stack.add(i);
  }
  stack.clear();
 
  // Calculate suffix sum array
  for (int i = n - 1; i > -1; i--) {
    int curr = arr[i];
    while (stack.size() != 0
           && arr[stack.get(stack.size() - 1)] >= curr) {
        stack.remove(stack.size() - 1);
    }
 
    if (stack.size() == 0)
      right[i] = (n - i) * (arr[i]);
 
    else {
      int small_idx = stack.get(stack.size() - 1);
      right[i] = right[small_idx]
        + (small_idx - i) * (arr[i]);
    }
    stack.add(i);
  }
  int ans = 0;
 
  for (int i = 0; i < n; i++) {
    int curr = left[i] + right[i] - arr[i];
    ans = Math.max(ans, curr);
  }
  return (ans);
}
 
// Driver code
public static void main(String[] args)
{
  int[] arr = { 5, 1, 8 };
  System.out.print(solve(arr));
 
}
}
 
// This code is contributed by 29AjayKumar.


Python3




# Python code to implement above approach
 
# Function to get the maximum cost
def solve(arr):
    n = len(arr)
    left = [0]*(n)
    right = [0]*(n)
    stack = []
 
    # Calculate left array
    for i in range(n):
        curr = arr[i]
        while(stack and
              arr[stack[-1]] >= curr):
            stack.pop()
 
        # Case 1
        if (len(stack) == 0):
            left[i] = (i + 1)*(arr[i])
 
        # Case 2
        else:
            small_idx = stack[-1]
            left[i] = left[small_idx] \
                     + (i - small_idx)*(arr[i])
        stack.append(i)
 
    stack.clear()
 
    # Calculate suffix sum array
    for i in range(n-1, -1, -1):
        curr = arr[i]
        while(stack and
              arr[stack[-1]] >= curr):
            stack.pop()
 
        if (len(stack) == 0):
            right[i] = (n-i)*(arr[i])
 
        else:
            small_idx = stack[-1]
            right[i] = right[small_idx] \
                     + (small_idx - i)*(arr[i])
 
        stack.append(i)
 
    ans = 0
 
    for i in range(n):
        curr = left[i] + right[i] - arr[i]
        ans = max(ans, curr)
 
    return (ans)
 
# Driver code
if __name__ == "__main__":
    arr = [5, 1, 8]
    print(solve(arr))


C#




// C# code for the above approach
using System;
using System.Collections.Generic;
class GFG {
 
  // Function to get the maximum cost
  static int solve(int[] arr)
  {
    int n = arr.Length;
    int[] left = new int[n];
    int[] right = new int[n];
    List<int> stack = new List<int>();
 
    // Calculate left array
    for (int i = 0; i < n; i++) {
      int curr = arr[i];
      while (stack.Count != 0
             && arr[stack[stack.Count - 1]] >= curr) {
        stack.RemoveAt(stack.Count - 1);
      }
 
      // Case 1
      if (stack.Count == 0)
        left[i] = (i + 1) * (arr[i]);
 
      // Case 2
      else {
        int small_idx = stack[stack.Count - 1];
        left[i] = left[small_idx]
          + (i - small_idx) * (arr[i]);
      }
      stack.Add(i);
    }
    stack.Clear();
 
    // Calculate suffix sum array
    for (int i = n - 1; i > -1; i--) {
      int curr = arr[i];
      while (stack.Count != 0
             && arr[stack[stack.Count - 1]] >= curr) {
        stack.RemoveAt(stack.Count - 1);
      }
 
      if (stack.Count == 0)
        right[i] = (n - i) * (arr[i]);
 
      else {
        int small_idx = stack[stack.Count - 1];
        right[i] = right[small_idx]
          + (small_idx - i) * (arr[i]);
      }
      stack.Add(i);
    }
    int ans = 0;
 
    for (int i = 0; i < n; i++) {
      int curr = left[i] + right[i] - arr[i];
      ans = Math.Max(ans, curr);
    }
    return (ans);
  }
 
  // Driver code
  public static void Main(string[] args)
  {
    int[] arr = { 5, 1, 8 };
    Console.WriteLine(solve(arr));
  }
}
 
// This code is contributed by ukasp.


Javascript




<script>
        // JavaScript code for the above approach
 
        // Function to get the maximum cost
        function solve(arr) {
            let n = arr.length
            let left = new Array(n).fill(0)
            let right = new Array(n).fill(0)
            let stack = []
 
            // Calculate left array
            for (let i = 0; i < n; i++) {
                curr = arr[i]
                while (stack.length != 0 &&
                    arr[stack[stack.length - 1]] >= curr) {
                    stack.pop()
                }
                // Case 1
                if (stack.length == 0)
                    left[i] = (i + 1) * (arr[i])
 
                // Case 2
                else {
                    small_idx = stack[stack.length - 1]
                    left[i] = left[small_idx]
                        + (i - small_idx) * (arr[i])
                }
                stack.push(i)
            }
            stack = []
 
            // Calculate suffix sum array
            for (let i = n - 1; i > -1; i--) {
                curr = arr[i]
                while (stack.length != 0 &&
                    arr[stack[stack.length - 1]] >= curr) {
                    stack.pop()
                }
 
                if (stack.length == 0)
                    right[i] = (n - i) * (arr[i])
 
                else {
                    small_idx = stack[stack.length - 1]
                    right[i] = right[small_idx]
                        + (small_idx - i) * (arr[i])
                }
                stack.push(i)
            }
            ans = 0
 
            for (let i = 0; i < n; i++) {
                curr = left[i] + right[i] - arr[i]
                ans = Math.max(ans, curr)
            }
            return (ans)
        }
         
        // Driver code
        arr = [5, 1, 8]
        document.write(solve(arr))
 
  // This code is contributed by Potta Lokesh
    </script>


Output

10

Time Complexity: O(N)
Auxiliary Space: O(N)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads