给定一个由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