Sum of maximum of all subarrays | Divide and Conquer

By | September 23, 2023

There is an array arr[] of length N. Your task is to find the sum of the minimum elements of each possible sub-array of the array.

Examples:

 
Input : arr[] = {2, 4, 6, 8, 10}
Output : 75
Explanation :
Min of all sub-arrays:
{2} - 2
{2, 4} - 2
{2, 4, 6} - 2
{2, 4, 6, 8} - 2
{2, 4, 6, 8, 10} - 2
{4} - 4
{4, 6} - 4
{4, 6, 8} - 4
{4, 6, 8, 10} - 4
{6} - 6
{6, 8} - 6
{6, 8, 10} - 6
{8} - 8
{8, 10} - 8
{10} - 10

Now, sum all the minimum values of subarrays:
2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 4 + 6 + 6 + 6 + 8 + 8 + 10 = 75
So, the output is 75.


Input : arr[] = {3, 5, 2, 7, 4}
Output : 41

We can solve this problem using O(N) approach using stack. In this article, we will learn how to solve this problem using divide and conquer. Let us assume that the element at ith index is the smallest among all. For any sub-array that has index ‘i’, the element at ‘i’ in the sub-array will always be the minimum. If the element at the ith index is the smallest, then we can safely say, that element at the ith index will be the minimum in the (i+1)*(N-i) subarray. So, its total contribution will be arr[i]*(i+1)*(N-i). Now, we will divide the array into two parts (0,i-1) and (i+1,N-1) and apply the same algorithm on both separately. So our general recurrence relation will be

minSumSubarray(arr, l, r) = arr[i]*(r-i+1)*(i-l+1) 
                            + minSumSubarray(arr, l, i-1)
                            + minSumSubarray(arr, i+1, r)
where i is index of minimum element in range [l,r].

Now, we need a way to answer rangemin() questions efficiently. Segmented trees would be an effective way to answer this question. We have to answer this question maximum N times. Thus, the time complexity of our divide and conquer algorithm will be O(Nlog(N)).

Let’s understand more using the code.

#include <bits/stdc++.h>
#define MAX_SEG_TREE_SIZE 51
using namespace std;

// Array to store the segment tree.
// In the first pair, we will store the minimum of a range.
// In the second pair, we will store the index of that range.
pair<int, int> seg_tree[MAX_SEG_TREE_SIZE];

// Size of the array declared globally to maintain simplicity in code
int n;

// Function to build the segment tree
pair<int, int> buildMinTree(int left, int right, int node, int arr[])
{
    // Base case
    if (left == right)
    {
        seg_tree[node] = {arr[left], left};
        return seg_tree[node];
    }

    // Finding the minimum among the left and right child
    seg_tree[node] = min(buildMinTree(left, (left + right) / 2, 2 * node + 1, arr),
                         buildMinTree((left + right) / 2 + 1, right, 2 * node + 2, arr));

    // Returning the minimum to the parent
    return seg_tree[node];
}

// Function to perform a range-min query in the segment tree
pair<int, int> rangeMin(int query_left, int query_right, int arr[],
                         int node = 0, int segment_left = 0, int segment_right = n - 1)
{
    // Base cases
    if (segment_right < query_left || segment_left > query_right)
        return {INT_MAX, -1};
    if (segment_left >= query_left && segment_right <= query_right)
        return seg_tree[node];

    // Finding the minimum among the left and right child
    return min(rangeMin(query_left, query_right, arr, 2 * node + 1, segment_left, (segment_left + segment_right) / 2),
               rangeMin(query_left, query_right, arr, 2 * node + 2, (segment_left + segment_right) / 2 + 1, segment_right));
}

// Function to find the minimum sum subarray
int minSumSubarray(int arr[], int left = 0, int right = n - 1)
{
    // Base case
    if (left > right)
        return 0;

    // Range-min query to determine the smallest element in the range.
    pair<int, int> min_range = rangeMin(left, right, arr);

    // Dividing the array into two parts
    return min_range.first * (right - min_range.second + 1) * (min_range.second - left + 1) +
           minSumSubarray(arr, left, min_range.second - 1) +
           minSumSubarray(arr, min_range.second + 1, right);
}

// Driver function
int main()
{
    // Input array
    int arr[] = {2, 4, 6, 8, 10};

    // Size of the array
    n = sizeof(arr) / sizeof(int);

    // Building the segment tree
    buildMinTree(0, n - 1, 0, arr);

    // Required answer
    cout << minSumSubarray(arr);

    return 0;
}

Output :

75

Time complexity : O(Nlog(N))

Author: Mithlesh Upadhyay

I hold an M.Tech degree in Artificial Intelligence (2023) from Delhi Technological University (DTU) and possess over 4 years of experience. I worked at GeeksforGeeks, leading teams and managing content, including GATE CS, Test Series, Placements, C, and C++. I've also contributed technical content to companies like MarsDev, Tutorialspoint, StudyTonight, TutorialCup, and Guru99. My skill set includes coding, Data Structures and Algorithms (DSA), and Object-Oriented Programming (OOPs). I'm proficient in C++, Python, JavaScript, HTML, CSS, Bootstrap, React.js, Node.js, MongoDB, Django, and Data Science.