📜  给定范围内的最大子阵列总和

📅  最后修改于: 2021-04-17 10:57:32             🧑  作者: Mango

给定一个由n个数字组成的数组,任务是回答以下查询:

maximumSubarraySum(start, end) : Find the maximum 
subarray sum in the range from array index 'start' 
to 'end'.

另请参阅:需要更新的范围查询

例子:

Input : arr[] = {1, 3, -4, 5, -2}
        Query 1: start = 0, end = 4
        Query 2: start = 0, end = 2
Output : 5
         4
Explanation:
For Query 1, [1, 3, -4, 5] or ( [5] ) 
represent the maximum sum sub arrays 
with sum = 5.

For Query 2, [1, 3] represents the 
maximum sum subarray in the query range
with sum = 4

段树可以用来解决这个问题,这里我们需要保留关于各种累计和的信息,在每个节点上我们存储以下内容:
1)最大前缀总和,
2)最大后缀总和,
3)总和,
4)最大子数组总和

每个节点都存储上述信息的经典分段树应足以查询每个查询。这里唯一的重点是如何将树的左右节点合并在一起。现在,我们将讨论如何使用其左,右子级的信息在每个段树节点中构造每个信息。

使用Left和Right子项构造最大前缀和

节点的最大前缀和可能有两种情况:

  1. 最大前缀和出现在左子节点中,
    In this Case,
    Maximum Prefix Sum = Maximum Prefix Sum of Left Child
    
  2. 最大前缀和包含左子元素的每个数组元素,以及有助于右子元素的最大前缀和的元素,
    In this Case,
    Maximum Prefix Sum = Total Sum of Left Child + 
                   Maximum Prefix Sum of Right Child
    

使用Left和Right子项构造最大后缀和

节点的最大后缀和可能有两种情况:

  1. 最大后缀总和出现在右孩子中,
    In this Case,
    Maximum Suffix Sum = Maximum Suffix Sum of Right Child
    
  2. 最大后缀总和包含Right子元素的每个数组元素,以及构成左子元素的最大后缀和的元素,

    In this Case,
    Maximum Suffix Sum = Total Sum of Right Child + 
                   Maximum Suffix Sum of Left Child
    

使用Left和Right子项构造最大子数组总和

节点的最大子数组和可能有以下三种情况:

  1. 最大子数组总和出现在左子元素中,
    In this Case,
    Maximum Sub-array Sum = Maximum Subarray Sum of Left Child
    
  2. 最大子数组总和出现在右子元素中,
    In this Case,
    Maximum Sub-array Sum = Maximum Subarray Sum of Right Child
    
  3. 最大子数组总和包含右侧子项的数组元素和右侧子项的最大后缀和,其中右侧子项的数组元素对右侧子项的最大前缀和有贡献,左子项的数组元素对左子项的最大后缀和有贡献,
    In this Case,
    Maximum Subarray Sum = Maximum Prefix Sum of Right Child                                           
                                      + 
                           Maximum Suffix Sum of Left Child
    
// C++ Program to Implement Maximum Sub-Array Sum in a range
#include 
using namespace std;
  
#define inf 0x3f3f
  
/* Node of the segment tree consisting of:
1. Maximum Prefix Sum,
2. Maximum Suffix Sum,
3. Total Sum,
4. Maximum Sub-Array Sum */
struct Node {
    int maxPrefixSum;
    int maxSuffixSum;
    int totalSum;
    int maxSubarraySum;
   
    Node()
    {
        maxPrefixSum = maxSuffixSum = maxSubarraySum = -inf;
        totalSum = -inf;
    }
};
   
// Returns Parent Node after merging its left and right child
Node merge(Node leftChild, Node rightChild)
{
    Node parentNode;
    parentNode.maxPrefixSum = max(leftChild.maxPrefixSum,
                                  leftChild.totalSum + 
                                  rightChild.maxPrefixSum);
   
    parentNode.maxSuffixSum = max(rightChild.maxSuffixSum,
                                  rightChild.totalSum +
                                  leftChild.maxSuffixSum);
   
    parentNode.totalSum = leftChild.totalSum +
                          rightChild.totalSum;
   
    parentNode.maxSubarraySum = max({leftChild.maxSubarraySum,
                                     rightChild.maxSubarraySum,
                                     leftChild.maxSuffixSum + 
                                     rightChild.maxPrefixSum});
   
    return parentNode;
}
   
// Builds the Segment tree recursively
void constructTreeUtil(Node* tree, int arr[], int start,
                                    int end, int index)
{
   
    /* Leaf Node */
    if (start == end) {
   
        // single element is covered under this range
        tree[index].totalSum = arr[start];
        tree[index].maxSuffixSum = arr[start];
        tree[index].maxPrefixSum = arr[start];
        tree[index].maxSubarraySum = arr[start];
        return;
    }
   
    // Recursively Build left and right children
    int mid = (start + end) / 2;
    constructTreeUtil(tree, arr, start, mid, 2 * index);
    constructTreeUtil(tree, arr, mid + 1, end, 2 * index + 1);
   
    // Merge left and right child into the Parent Node
    tree[index] = merge(tree[2 * index], tree[2 * index + 1]);
}
   
/* Function to construct segment tree from given array. 
   This function allocates memory for segment tree and 
   calls constructTreeUtil() to fill the allocated 
   memory */
Node* constructTree(int arr[], int n)
{
    // Allocate memory for segment tree
    int x = (int)(ceil(log2(n))); // Height of the tree
   
    // Maximum size of segment tree 
    int max_size = 2 * (int)pow(2, x) - 1; 
    Node* tree = new Node[max_size];
   
    // Fill the allocated memory tree
    constructTreeUtil(tree, arr, 0, n - 1, 1);
   
    // Return the constructed segment tree
    return tree;
}
   
/* A Recursive function to get the desired 
   Maximum Sum Sub-Array,
The following are parameters of the function-
   
tree     --> Pointer to segment tree 
index --> Index of the segment tree Node 
ss & se  --> Starting and ending indexes of the 
             segment represented by
                 current Node, i.e., tree[index]
qs & qe  --> Starting and ending indexes of query range */
Node queryUtil(Node* tree, int ss, int se, int qs,
                               int qe, int index)
{
    // No overlap
    if (ss > qe || se < qs) {
   
        // returns a Node for out of bounds condition
        Node nullNode;
        return nullNode;
    }
   
    // Complete overlap
    if (ss >= qs && se <= qe) {
        return tree[index];
    }
   
    // Partial Overlap Merge results of Left 
    // and Right subtrees
    int mid = (ss + se) / 2;
    Node left = queryUtil(tree, ss, mid, qs, qe, 
                                     2 * index);
    Node right = queryUtil(tree, mid + 1, se, qs, 
                              qe, 2 * index + 1);
   
    // merge left and right subtree query results
    Node res = merge(left, right);
    return res;
}
   
/* Returns the Maximum Subarray Sum between start and end
   It mainly uses queryUtil(). */
int query(Node* tree, int start, int end, int n)
{
    Node res = queryUtil(tree, 0, n - 1, start, end, 1);
    return res.maxSubarraySum;
}
   
int main()
{
    int arr[] = { 1, 3, -4, 5, -2 };
    int n = sizeof(arr) / sizeof(arr[0]);
   
    // Construct Segment Tree
    Node* Tree = constructTree(arr, n);
    int start, end, maxSubarraySum;
   
    // Answering query 1:
    start = 0;
    end = 4;
    maxSubarraySum = query(Tree, start, end, n);
    cout << "Maximum Sub-Array Sum between "
         << start << " and " << end
         << " = " << maxSubarraySum << "\n";
   
    // Answering query 2:
    start = 0;
    end = 2;
    maxSubarraySum = query(Tree, start, end, n);
    cout << "Maximum Sub-Array Sum between "
         << start << " and " << end
         << " = " << maxSubarraySum << "\n";
   
    return 0;
}
输出:
Maximum Sub-Array Sum between 0 and 4 = 5
Maximum Sub-Array Sum between 0 and 2 = 4

时间复杂度:每个查询为O(logn)。