📜  所有子数组的最大值之和 |分而治之

📅  最后修改于: 2021-09-16 11:07:30             🧑  作者: Mango

给定一个长度为 N 的数组 arr[],任务是找到该数组的每个可能子数组的最大元素的总和。
例子:

Input : arr[] = {1, 3, 1, 7}
Output : 42
Max of all sub-arrays:
{1} - 1
{1, 3} - 3
{1, 3, 1} - 3
{1, 3, 1, 7} - 7
{3} - 3
{3, 1} - 3
{3, 1, 7} - 7 
{1} - 1
{1, 7} - 7
{7} - 7
1 + 3 + 3 + 7 + 3 + 3 + 7 + 1 + 7 + 7 = 42

Input : arr[] = {1, 1, 1, 1, 1}
Output : 15

我们已经在本文中讨论了使用堆栈解决此问题的 O(N) 方法。
方法 :
在本文中,我们将学习如何使用分治法来解决这个问题。
让我们假设i索引处的元素是最大的。对于包含索引“i”的任何子数组,“i”处的元素将始终是子数组中的最大值。
如果i索引处的元素最大,我们可以有把握地说,第 i 个索引的元素在 (i+1)*(Ni) 个子数组中最大。因此,它的总贡献将是 arr[i]*(i+1)*(Ni)。现在,我们将数组分为两部分,(0, i-1) 和 (i+1, N-1) 并分别对它们应用相同的算法。
所以我们一般的递推关系将是:

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

现在,我们需要一种有效回答 rangeMax() 查询的方法。段树将是回答此查询的有效方法。我们最多需要回答这个查询 N 次。因此,我们的分治算法的时间复杂度为 O(Nlog(N))。
如果我们必须回答“所有子数组的最小值之和”这个问题,那么我们将使用段树来回答 rangeMin() 查询。为此,您可以查看文章段树范围的最小值。
下面是实现代码:

C++
// C++ implementation of the above approach
 
#include 
#define seg_max 51
using namespace std;
 
// Array to store segment tree.
// In first we will store the maximum
// of a range
// In second, we will store index of
// that range
pair seg_tree[seg_max];
 
// Size of array declared global
// to maintain simplicity in code
int n;
 
// Function to build segment tree
pair buildMaxTree(int l, int r, int i, int arr[])
{
    // Base case
    if (l == r) {
        seg_tree[i] = { arr[l], l };
        return seg_tree[i];
    }
 
    // Finding the maximum among left and right child
    seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
                      buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
 
    // Returning the maximum to parent
    return seg_tree[i];
}
 
// Function to perform range-max query in segment tree
pair rangeMax(int l, int r, int arr[],
                        int i = 0, int sl = 0, int sr = n - 1)
{
    // Base cases
    if (sr < l || sl > r)
        return { INT_MIN, -1 };
    if (sl >= l and sr <= r)
        return seg_tree[i];
 
    // Finding the maximum among left and right child
    return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
               rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
}
 
// Function to find maximum sum subarray
int maxSumSubarray(int arr[], int l = 0, int r = n - 1)
{
    // base case
    if (l > r)
        return 0;
 
    // range-max query to determine
    // largest in the range.
    pair a = rangeMax(l, r, arr);
 
    // divide the array in two parts
    return a.first * (r - a.second + 1) * (a.second - l + 1)
           + maxSumSubarray(arr, l, a.second - 1)
           + maxSumSubarray(arr, a.second + 1, r);
}
 
// Driver Code
int main()
{
    // Input array
    int arr[] = { 1, 3, 1, 7 };
 
    // Size of array
    n = sizeof(arr) / sizeof(int);
 
    // Builind the segment-tree
    buildMaxTree(0, n - 1, 0, arr);
 
    cout << maxSumSubarray(arr);
 
    return 0;
}


Java
// Java implementation of the above approach
class GFG {
    static class pair {
        int first, second;
 
        public pair(int first, int second) {
            this.first = first;
            this.second = second;
        }
    }
 
    static final int seg_max = 51;
     
    // Array to store segment tree.
    // In first we will store the maximum
    // of a range
    // In second, we will store index of
    // that range
    static pair[] seg_tree = new pair[seg_max];
 
    // Size of array declared global
    // to maintain simplicity in code
    static int n;
 
    // Function to build segment tree
    static pair buildMaxTree(int l, int r, int i, int arr[])
    {
        // Base case
        if (l == r) {
            seg_tree[i] = new pair(arr[l], l);
            return seg_tree[i];
        }
 
        // Finding the maximum among left and right child
        seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
                buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
 
        // Returning the maximum to parent
        return seg_tree[i];
    }
 
    // Function to perform range-max query in segment tree
    static pair rangeMax(int l, int r, int arr[],
                        int i, int sl, int sr)
    {
        // Base cases
        if (sr < l || sl > r)
            return new pair(Integer.MIN_VALUE, -1);
        if (sl >= l && sr <= r)
            return seg_tree[i];
 
        // Finding the maximum among left and right child
        return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
                rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
    }
 
    static pair max(pair f, pair s) {
        if (f.first > s.first)
            return f;
        else
            return s;
    }
 
    // Function to find maximum sum subarray
    static int maxSumSubarray(int arr[], int l, int r)
    {
        // base case
        if (l > r)
            return 0;
 
        // range-max query to determine
        // largest in the range.
        pair a = rangeMax(l, r, arr, 0, 0, n - 1);
 
        // divide the array in two parts
        return a.first * (r - a.second + 1) * (a.second - l + 1)
                + maxSumSubarray(arr, l, a.second - 1)
                + maxSumSubarray(arr, a.second + 1, r);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Input array
        int arr[] = { 1, 3, 1, 7 };
 
        // Size of array
        n = arr.length;
 
        // Builind the segment-tree
        buildMaxTree(0, n - 1, 0, arr);
 
        System.out.print(maxSumSubarray(arr, 0, n - 1));
    }
}
 
// This code is contributed by 29AjayKumar


C#
// C# implementation of the above approach
using System;
 
class GFG {
    class pair {
        public int first, second;
  
        public pair(int first, int second) {
            this.first = first;
            this.second = second;
        }
    }
  
    static readonly int seg_max = 51;
      
    // Array to store segment tree.
    // In first we will store the maximum
    // of a range
    // In second, we will store index of
    // that range
    static pair[] seg_tree = new pair[seg_max];
  
    // Size of array declared global
    // to maintain simplicity in code
    static int n;
  
    // Function to build segment tree
    static pair buildMaxTree(int l, int r, int i, int []arr)
    {
        // Base case
        if (l == r) {
            seg_tree[i] = new pair(arr[l], l);
            return seg_tree[i];
        }
  
        // Finding the maximum among left and right child
        seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
                buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
  
        // Returning the maximum to parent
        return seg_tree[i];
    }
  
    // Function to perform range-max query in segment tree
    static pair rangeMax(int l, int r, int []arr,
                        int i, int sl, int sr)
    {
        // Base cases
        if (sr < l || sl > r)
            return new pair(int.MinValue, -1);
        if (sl >= l && sr <= r)
            return seg_tree[i];
  
        // Finding the maximum among left and right child
        return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
                rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
    }
  
    static pair max(pair f, pair s) {
        if (f.first > s.first)
            return f;
        else
            return s;
    }
  
    // Function to find maximum sum subarray
    static int maxSumSubarray(int []arr, int l, int r)
    {
        // base case
        if (l > r)
            return 0;
  
        // range-max query to determine
        // largest in the range.
        pair a = rangeMax(l, r, arr, 0, 0, n - 1);
  
        // divide the array in two parts
        return a.first * (r - a.second + 1) * (a.second - l + 1)
                + maxSumSubarray(arr, l, a.second - 1)
                + maxSumSubarray(arr, a.second + 1, r);
    }
  
    // Driver Code
    public static void Main(String[] args)
    {
        // Input array
        int []arr = { 1, 3, 1, 7 };
  
        // Size of array
        n = arr.Length;
  
        // Builind the segment-tree
        buildMaxTree(0, n - 1, 0, arr);
  
        Console.Write(maxSumSubarray(arr, 0, n - 1));
    }
}
 
// This code is contributed by PrinciRaj1992


Javascript


输出:

42

时间复杂度:O(Nlog(N))

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程