📌  相关文章
📜  给定范围的最大前缀和

📅  最后修改于: 2021-04-16 06:13:08             🧑  作者: Mango

给定一个由n个整数和q个查询组成的数组,每个查询的范围从l到r。找到范围l – r的最大前缀和。
例子:

Input: a[] = {-1, 2, 3, -5} 
       q = 2
       l = 0, r = 3
       l = 1, r = 3

Output: 4
        5

Explanation:- The range (0, 3) in the 1st query has
              [-1, 2, 3, -5], since it is prefix, 
              we have to start from -1. Hence, the 
              max prefix sum will be -1 + 2 + 3 = 4.
              The range (1, 3) in the 2nd query has 
              [2, 3, -5], since it is prefix, we 
              have to start from 2. Hence, the max
              prefix sum will be 2 + 3 = 5.

Input: a[] = {-2, -3, 4, -1, -2, 1, 5, -3} 
       q = 1
       l = 1 r = 7 

Output: 4

Explanation:- The range (1, 7) in the 1st query has 
              [-3, 4, -1, -2, 1, 5, -3], since it is
              prefix, we have to start from -3. 
              Hence, the max prefix sum will be 
              -3 + 4 - 1 - 2 + 1 + 5 = 4.

正常方法:
一个简单的解决方案是运行从l到r的循环,并为每个查询计算从l到r的最大前缀和。
时间复杂度:O(q * n),
辅助空间:O(1)
高效方法:
一种有效的方法是构建一个分段树,其中每个节点存储两个值(sum和prefix_sum),并对其进行范围查询以找到最大前缀和。但是对于构建段树,我们必须考虑在树的节点上存储什么。
为了找出最大前缀和,我们需要做两件事,一个是和,而另一个是前缀和。合并将返回两件事,即范围的总和和将在段树中存储max(prefix.left,prefix.sum + prefix.right)的前缀总和。
如果我们深入研究,则任意两个范围组合的最大前缀总和要么是左侧的前缀总和,要么是左侧的总和加上右侧的前缀总和,以最大值为准。
段树的表示形式:
1.叶节点是输入数组的元素。
2.每个内部节点代表叶节点的某些合并。对于不同的问题,合并可能会有所不同。对于此问题,合并是节点下的叶子之和。
树的数组表示形式用于表示段树。对于索引i处的每个节点,左子节点在索引2 * i + 1处,右子节点在索引2 * i + 2处,父节点在(i-1)/ 2处。
从给定数组构造细分树:
我们从一个段arr [0开始。 。 。 n-1]。每次我们将当前段分为两半(如果尚未变成长度为1的段),然后在这两个半段上调用相同的过程,则对于每个这样的段,我们将总和和前缀总和存储在相应的节点中。
然后,我们在段树上进行范围查询,以找出给定范围的最大前缀和,并输出最大前缀和。
下面是上述方法的C++实现。

CPP
// CPP program to find
// maximum prefix sum
#include 
using namespace std;
 
// struct two store two values in one node
struct Node {
    int sum;
    int prefix;
};
 
Node tree[4 * 10000];
 
// function to build the segment tree
void build(int a[], int index, int beg, int end)
{
    if (beg == end) {
 
        // If there is one element in array,
        // store it in current node of
        // segment tree
        tree[index].sum = a[beg];
        tree[index].prefix = a[beg];
    } else {
        int mid = (beg + end) / 2;
 
        // If there are more than one elements,
        // then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid);
        build(a, 2 * index + 2, mid + 1, end);
 
        // adds the sum and stores in the index
        // position of segment tree
        tree[index].sum = tree[2 * index + 1].sum +
                          tree[2 * index + 2].sum;
 
        // stores the max of prefix-sum either
        // from right or from left.
        tree[index].prefix = max(tree[2 * index + 1].prefix,
                                 tree[2 * index + 1].sum +
                                tree[2 * index + 2].prefix);
    }
}
 
// function to do the range query in the segment
// tree for the maximum prefix sum
Node query(int index, int beg, int end, int l, int r)
{
    Node result;
    result.sum = result.prefix = -1;
 
    // If segment of this node is outside the given
    // range, then return the minimum value.
    if (beg > r || end < l)
        return result;
 
    // If segment of this node is a part of given
    // range, then return the node of the segment
    if (beg >= l && end <= r)
        return tree[index];
 
    int mid = (beg + end) / 2;
 
    // if left segment of this node falls out of
    // range, then recur in the right side of
    // the tree
    if (l > mid)
        return query(2 * index + 2, mid + 1, end,
                                         l, r);
 
    // if right segment of this node falls out of
    // range, then recur in the left side of
    // the tree
    if (r <= mid)
        return query(2 * index + 1, beg, mid,
                                       l, r);
 
    // If a part of this segment overlaps with
    // the given range
    Node left = query(2 * index + 1, beg, mid,
                                        l, r);
    Node right = query(2 * index + 2, mid + 1,
                                   end, l, r);
 
    // adds the sum of the left and right
    // segment
    result.sum = left.sum + right.sum;
 
    // stores the max of prefix-sum
    result.prefix = max(left.prefix, left.sum +
                            right.prefix);
 
    // returns the value
    return result;
}
 
// driver program to test the program
int main()
{
 
    int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
 
    // calculates the length of array
    int n = sizeof(a) / sizeof(a[0]);
 
    // calls the build function to build
    // the segment tree
    build(a, 0, 0, n - 1);
 
    // find the max prefix-sum between
    // 3rd and 5th index of array
    cout << query(0, 0, n - 1, 3, 5).prefix
         << endl;
 
    // find the max prefix-sum between
    // 0th and 7th index of array
    cout << query(0, 0, n - 1, 1, 7).prefix
         << endl;
 
    return 0;
}


Java
// JAVA program to find
// maximum prefix sum
class GFG
{
 
// two store two values in one node
static class Node
{
    int sum;
    int prefix;
};
 
static Node []tree = new Node[4 * 10000];
static
{
    for(int i = 0; i < tree.length; i++)
        tree[i] = new Node();
}
 
// function to build the segment tree
static void build(int a[], int index, int beg, int end)
{
    if (beg == end)
    {
 
        // If there is one element in array,
        // store it in current node of
        // segment tree
        tree[index].sum = a[beg];
        tree[index].prefix = a[beg];
    } else {
        int mid = (beg + end) / 2;
 
        // If there are more than one elements,
        // then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid);
        build(a, 2 * index + 2, mid + 1, end);
 
        // adds the sum and stores in the index
        // position of segment tree
        tree[index].sum = tree[2 * index + 1].sum +
                        tree[2 * index + 2].sum;
 
        // stores the max of prefix-sum either
        // from right or from left.
        tree[index].prefix = Math.max(tree[2 * index + 1].prefix,
                                tree[2 * index + 1].sum +
                                tree[2 * index + 2].prefix);
    }
}
 
// function to do the range query in the segment
// tree for the maximum prefix sum
static Node query(int index, int beg, int end, int l, int r)
{
    Node result = new Node();
    result.sum = result.prefix = -1;
 
    // If segment of this node is outside the given
    // range, then return the minimum value.
    if (beg > r || end < l)
        return result;
 
    // If segment of this node is a part of given
    // range, then return the node of the segment
    if (beg >= l && end <= r)
        return tree[index];
 
    int mid = (beg + end) / 2;
 
    // if left segment of this node falls out of
    // range, then recur in the right side of
    // the tree
    if (l > mid)
        return query(2 * index + 2, mid + 1, end,
                                        l, r);
 
    // if right segment of this node falls out of
    // range, then recur in the left side of
    // the tree
    if (r <= mid)
        return query(2 * index + 1, beg, mid,
                                    l, r);
 
    // If a part of this segment overlaps with
    // the given range
    Node left = query(2 * index + 1, beg, mid,
                                        l, r);
    Node right = query(2 * index + 2, mid + 1,
                                end, l, r);
 
    // adds the sum of the left and right
    // segment
    result.sum = left.sum + right.sum;
 
    // stores the max of prefix-sum
    result.prefix = Math.max(left.prefix, left.sum +
                            right.prefix);
 
    // returns the value
    return result;
}
 
// Driver code
public static void main(String[] args)
{
 
    int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
 
    // calculates the length of array
    int n = a.length;
 
    // calls the build function to build
    // the segment tree
    build(a, 0, 0, n - 1);
 
    // find the max prefix-sum between
    // 3rd and 5th index of array
    System.out.print(query(0, 0, n - 1, 3, 5).prefix
        +"\n");
 
    // find the max prefix-sum between
    // 0th and 7th index of array
    System.out.print(query(0, 0, n - 1, 1, 7).prefix
        +"\n");
}
}
 
// This code is contributed by PrinciRaj1992


C#
// C# program to find
// maximum prefix sum
using System;
 
class GFG
{
 
// two store two values in one node
class Node
{
    public int sum;
    public int prefix;
};
 
static Node []tree = new Node[4 * 10000];
 
// function to build the segment tree
static void build(int []a, int index, int beg, int end)
{
    if (beg == end)
    {
 
        // If there is one element in array,
        // store it in current node of
        // segment tree
        tree[index].sum = a[beg];
        tree[index].prefix = a[beg];
    } else {
        int mid = (beg + end) / 2;
 
        // If there are more than one elements,
        // then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid);
        build(a, 2 * index + 2, mid + 1, end);
 
        // adds the sum and stores in the index
        // position of segment tree
        tree[index].sum = tree[2 * index + 1].sum +
                        tree[2 * index + 2].sum;
 
        // stores the max of prefix-sum either
        // from right or from left.
        tree[index].prefix = Math.Max(tree[2 * index + 1].prefix,
                                tree[2 * index + 1].sum +
                                tree[2 * index + 2].prefix);
    }
}
 
// function to do the range query in the segment
// tree for the maximum prefix sum
static Node query(int index, int beg, int end, int l, int r)
{
    Node result = new Node();
    result.sum = result.prefix = -1;
 
    // If segment of this node is outside the given
    // range, then return the minimum value.
    if (beg > r || end < l)
        return result;
 
    // If segment of this node is a part of given
    // range, then return the node of the segment
    if (beg >= l && end <= r)
        return tree[index];
 
    int mid = (beg + end) / 2;
 
    // if left segment of this node falls out of
    // range, then recur in the right side of
    // the tree
    if (l > mid)
        return query(2 * index + 2, mid + 1, end,
                                        l, r);
 
    // if right segment of this node falls out of
    // range, then recur in the left side of
    // the tree
    if (r <= mid)
        return query(2 * index + 1, beg, mid,
                                    l, r);
 
    // If a part of this segment overlaps with
    // the given range
    Node left = query(2 * index + 1, beg, mid,
                                        l, r);
    Node right = query(2 * index + 2, mid + 1,
                                end, l, r);
 
    // adds the sum of the left and right
    // segment
    result.sum = left.sum + right.sum;
 
    // stores the max of prefix-sum
    result.prefix = Math.Max(left.prefix, left.sum +
                            right.prefix);
 
    // returns the value
    return result;
}
 
// Driver code
public static void Main(String[] args)
{
 
    for(int i = 0; i < tree.Length; i++)
        tree[i] = new Node();
    int []a = { -2, -3, 4, -1, -2, 1, 5, -3 };
 
    // calculates the length of array
    int n = a.Length;
 
    // calls the build function to build
    // the segment tree
    build(a, 0, 0, n - 1);
 
    // find the max prefix-sum between
    // 3rd and 5th index of array
    Console.Write(query(0, 0, n - 1, 3, 5).prefix
        +"\n");
 
    // find the max prefix-sum between
    // 0th and 7th index of array
    Console.Write(query(0, 0, n - 1, 1, 7).prefix
        +"\n");
}
}
 
// This code is contributed by Rajput-Ji


Python3
# Python3 program to find
# maximum prefix sum
 
# struct two store two values in one node
 
# function to build the segment tree
def build(a, index, beg, end):
    global tree
 
    if (beg == end):
 
        # If there is one element in array,
        # store it in current node of
        # segment tree
        tree[index][0] = a[beg]
        tree[index][1] = a[beg]
    else:
        mid = (beg + end) // 2
 
        # If there are more than one elements,
        # then recur for left and right subtrees
        build(a, 2 * index + 1, beg, mid)
        build(a, 2 * index + 2, mid + 1, end)
 
        # adds the sum and stores in the index
        # position of segment tree
        tree[index][0] = tree[2 * index + 1][0] + tree[2 * index + 2][0]
 
        # stores the max of prefix-sum either
        # from right or from left.
        tree[index][1] = max(tree[2 * index + 1][1],tree[2 * index + 1][0] + tree[2 * index + 2][1])
 
# function to do the range query in the segment
# tree for the maximum prefix sum
def query(index, beg, end, l, r):
    global tree
    result = [-1, -1]
    # result[0] = result[1] = -1
 
    # If segment of this node is outside the given
    # range, then return the minimum value.
    if (beg > r or end < l):
        return result
 
    # If segment of this node is a part of given
    # range, then return the node of the segment
    if (beg >= l and end <= r):
        return tree[index]
 
    mid = (beg + end) // 2
 
    # if left segment of this node falls out of
    # range, then recur in the right side of
    # the tree
    if (l > mid):
        return query(2 * index + 2, mid + 1, end, l, r)
 
    # if right segment of this node falls out of
    # range, then recur in the left side of
    # the tree
    if (r <= mid):
        return query(2 * index + 1, beg, mid, l, r)
 
    # If a part of this segment overlaps with
    # the given range
    left = query(2 * index + 1, beg, mid, l, r)
    right = query(2 * index + 2, mid + 1, end, l, r)
 
    # adds the sum of the left and right
    # segment
    result[0] = left[0] + right[0]
 
    # stores the max of prefix-sum
    result[1] = max(left[1], left[0] + right[1])
 
    # returns the value
    return result
 
 
# driver program to test the program
if __name__ == '__main__':
 
    a = [-2, -3, 4, -1, -2, 1, 5, -3 ]
 
    tree = [[0,0] for i in range(4 * 10000)]
 
    # calculates the length of array
    n = len(a)
 
    # calls the build function to build
    # the segment tree
    build(a, 0, 0, n - 1)
 
    # find the max prefix-sum between
    # 3rd and 5th index of array
    print(query(0, 0, n - 1, 3, 5)[1])
 
    # find the max prefix-sum between
    # 0th and 7th index of array
    print(query(0, 0, n - 1, 1, 7)[1])
 
    # This code is contributed by mohit kumar 29.


输出:

-1
4