【问题标题】:Find minimum value at each index after queries which tell you minimum value over a range在查询告诉您某个范围内的最小值之后,在每个索引处查找最小值
【发布时间】:2015-07-12 13:36:58
【问题描述】:

假设最初在数组a 中,每个元素的值都是无穷大。

现在M 查询是l r x 类型的输入。

这里lra[i]>x 需要更新值的范围,其中l<=i<=rl<=r<=n

M查询后需要在每个索引处输出最小值。

一种方法是使用蛮力

memset(a,inf,sizeof(a));
while(j<m)
{
     scanf("%d %d %d",&l,&r,&c);
    for(i=l-1;i<r;i++)
    {
        if(a[i]>c)
            a[i]=c;
    }

    j++;
}

for(i=0;i<n;i++)
    printf("%d",a[i]);

现在这需要 O(mn) 时间,其中 n=每个查询的大小,在最坏的情况下可能是 n。

有哪些更有效的方法可以以较低的时间复杂度解决这个问题?

【问题讨论】:

  • "M次查询后需要输出每个索引处的最小值。"你能详细说明一下吗?你的伪代码没有输出任何东西。
  • 这只是意味着在 M 次查询之后,每个索引应该具有最小的可能值。我已经进行了修改。
  • 在倒数第二句中你说n 在最坏的情况下可以是n!?
  • @Ulrich 我的意思是说,在最坏的情况下,范围的大小等于数组的大小。
  • 请编辑您的问题,而不是作为评论回答。

标签: c++ c algorithm


【解决方案1】:

有一种方法具有不同的渐近复杂度。它涉及保留查询开始和结束的排序列表。为了避免实际排序,我使用了一个大小为a 的稀疏数组。

现在,一般的想法是您存储查询并在迭代时保留一个包含查询的堆是您的范围:

# size of array (n)
count = ...
# for each array element you have a list of ranges that
# start or end at this array element
list<queries> l[count]
list<queries> r[count]
heap<queries> h
for i in range(count):
    if l[i]:
        h.push(l[i])
    if h is empty:
        output(inf)
    else:
        output(h.lowest().value)
    if r[i]:
        h.pop(r[i])

这个(和其他算法)的实际性能很大程度上取决于数组的大小和查询的密度,但是这个算法的渐近复杂度没有涵盖这些。在忽略实际输入数据的情况下,无法找到最佳算法。根据数据改变算法也是值得的。

【讨论】:

  • 请注意,这假定问题是离线的。如果您必须在每次更新到达时执行它,您就不能这样做。当然,对于手头的问题,它可能是离线的,因为最后只有一个查询,所以 +1。
  • 没错,@IVlad。描述和示例代码向我表明,处理查询和给出结果是两个独立的阶段。
【解决方案2】:

注意:我的回答假设问题是在线的,因此您必须在更新和查询到达时执行它们。这样做的一个优点是我的解决方案更健壮,允许您以相同的复杂性添加更多类型的更新和查询。缺点是,如果您要处理离线问题,它可能不是解决您问题的绝对最佳选择。


您可以使用segment tree。让段树中的每个节点存储为其关联间隔设置的最小值(最初为无穷大,非常大),并使用惰性更新和查询方案。

更新(左、右、c)

Update(node, left, right, c):
  if node.interval does not intersect [left, right]:
    return
  if node.interval included in [left, right]:
    node.minimum = min(c, node.minimum)
    return

  Update(node.left, left, right, c)
  Update(node.right, left, right, c)

查询(索引)

Query(node, minimum = infinity, index):
  if node.interval == [index, index]:
    return minimum

  if index included in node.left.interval:
    return Query(node.left, min(minimum, node.minimum), index)

  return Query(node.right, min(minimum, node.minimum), index)

总复杂度:O(log n) 用于每个更新和查询操作。最后你需要为每个元素调用Query

【讨论】:

  • 是否有必要使用惰性更新来提高复杂性?
  • @user3933528 是的。如果您在Update 函数的第二个if 条件中消除return,则更新复杂度将为O(n)。当然,您可以在 O(n) 而不是 O(n log n) 中构建最终答案,但您的算法也是如此。
  • 如果不使用惰性传播,我认为这相当于使用幼稚的方法。
  • 你是否在查询中包含了惰性传播,因为我不知道如何实现它。
  • @user3933528 在我的伪代码中使用了惰性传播。这只是一个花哨的术语,在实现方面它基本上只是我告诉你的回报。如果您按照我发布的那样实现伪代码,您将获得高效的O(log n) / query 算法。
猜你喜欢
  • 2015-10-25
  • 2021-09-19
  • 1970-01-01
  • 2018-07-27
  • 2019-02-11
  • 2017-05-08
  • 2019-11-08
  • 1970-01-01
  • 2018-06-19
相关资源
最近更新 更多