📜  所有子数组的总和|分而治之

📅  最后修改于: 2021-05-04 16:59:38             🧑  作者: 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 + 1)*(Ni)个子数组中第i个元素的索引最大。因此,其总贡献为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


输出:
42

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