我想找到给定数组中最大的和连续子数组。我知道使用Kadane算法使用动态规划的概念找到最大和连续子阵列方法的O(n)方法。
但是如果范围查询的数量非常大,则需要花费很多时间。有没有办法使用Segment-Trees来解决它,因为它是回答范围查询的首选方案,它在O(log(n))时间内解决。 谢谢。
答案 0 :(得分:6)
根据我对Justin的回答的评论,您可以扩充标准的分段树以实现O(log(n))
查询时间O(n log(n))
时间来构建树,即将所有n个元素插入树中。< / p>
我们的想法是在每个节点v
中存储不只是一个值,而是四个:
- max_value [v]:= v`s子树中的最大连续总和
- left_value [v]:=与v子树对应的范围左边界附近的最大连续和
- right_value [v]:=与v子树相对应的范围右边界附近的最大连续和
- sum [v]:= v子树中所有元素的总和
醇>
要为节点v
执行更新操作,您必须重新计算max_value[v], left_value[v], right_value[v], sum[v]
。这非常简单,我认为你可以自己解决这个问题 - 有几个案例需要考虑。
查询操作类似于基本段树中的查询操作。唯一的区别是,在这种情况下,您在计算结果时还必须考虑left_value[v]
和right_value[v]
- 同样,还有一些容易考虑的案例。
我希望你能算出遗漏的细节。如果没有,请告诉我,我会给出更详细的解释。
答案 1 :(得分:1)
虽然@ pkacprzak的答案很好地描述了解决方案,但有些人可能更喜欢代码示例。
#include <iostream>
#define ll long long
using namespace std;
const int N=1<<17; // big power of 2 that fits your data
int n,k;
struct P {ll l, r, ts, bs;}; // left, right, totalsum, bestsum
P p[2*N];
ll maxf(ll a,ll b,ll c) {return max(a,max(b,c));}
P combine(P &cl,P &cr) {
P node;
node.ts = cl.ts + cr.ts;
node.l = maxf(cl.l, cl.ts, cl.ts + cr.l);
node.r = maxf(cr.r, cr.ts, cr.ts + cl.r);
node.bs = maxf(cl.bs, cr.bs, cl.r + cr.l);
return node;
}
void change(int k, ll x) {
k += N;
p[k].l = p[k].r = p[k].ts = p[k].bs = x;
for (k /= 2; k >= 1; k /= 2) {
p[k] = combine(p[2*k], p[2*k+1]);
}
}
要在细分树中添加/更改值,请使用change(k, x)
(每次调用O(log(n))
),其中k是位置,x是值。每次调用p[1].bs
后,可以从change
(树的顶部)读取最大子阵列的总和。
如果您还需要找到子阵列的确切索引,您可以使用迭代查询在O(log(n))
或O(log^2(n))
的二进制搜索中执行递归自上而下查询。
编辑:如果我们对给定子阵列的最大子阵列感兴趣,最好构建一个递归的自顶向下查询。参见:
https://www.quora.com/How-do-I-calculate-the-maximum-sub-segment-sum-in-a-segment-tree
总而言之,细分树可以通过更改数据和来处理此问题,并更改我们感兴趣的范围。