在HackerRank的Array Manipulation背后使用的逻辑

时间:2018-01-09 06:00:52

标签: c++ arrays

我无法掌握Hackerrank问题解决方案背后的逻辑,https://www.hackerrank.com/challenges/crush/problem

在讨论部分,许多人也发布了他们的解决方案,但我无法理解为什么这种逻辑有效。

以下解决方案取自相同问题的讨论部分,并且具有最大数量的upvotes,

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;


int main() {
    long int N,K,p,q,sum,i,j,max=0,x=0;

    cin>>N>>K;
    long int *a=new long int[N+1]();

    for(i=0;i<K;i++)
    {
        cin>>p>>q>>sum;
        a[p]+=sum;
        if((q+1)<=N) a[q+1]-=sum;
    }

    for(i=1;i<=N;i++)
    {
       x=x+a[i];
       if(max<x) max=x;

    }

    cout<<max;
    return 0;
}

有人可以解释一下背后的逻辑吗? 非常感谢您的帮助。

8 个答案:

答案 0 :(得分:25)

我们基本上将增量存储在范围中的起始和最后一个元素之后。对于a b k,我们将为索引+k中的所有元素增加[a,b],但接下来的元素不会增加。所以我们减去它,因为w.r.t前一个增量范围右边的所有元素都会减去-k。我们基本上通过这个增量/减量存储所有最终值。

最后,我们正在从左到右计算元素。 如果你想的更深一点,那就是存储一个元素比前一个元素大一点。

最初,数组将为0 0 0 0 0

在第一次操作1 3 3之后,数组元素应该是 3 3 3 0 0但我们将其存储为

3 0 0 -3 0

含义

First element is 3 greater than 0.
Second  ->       0 greater than index 1 element.
Third   ->       0 greater than index 2 element
Fourth  ->      -3 greater than index 3 element.
fifth   ->       0 greater than index 4 element.

在第二次操作2 4 4之后,数组最初将为3 7 7 4 0,但我们将其存储为3 4 0 -3 -4。就像我详细描述的那样,请记住并以这种方式思考,你会发现我们并没有丢失信息。我们只是以不同的方式存储它。

最终值

0+(3) 0+3+(4) 0+3+4+(0) 0+3+4+0+(-3) 0+3+4+0-3+(-4)

3  7    7       4           0  matches with what we got earlier.

注意我们如何计算每个元素。只需添加前一个元素,其中当前元素的值更大。

请注意,此解决方案有效,因为仅查询一次。如果它被查询m次,那么这个消息不起作用。然后,您必须使用segment treebinary indexed tree深入挖掘。

答案 1 :(得分:3)

我会试着解释一下我对此的理解:
每行输入基本上都描述了一个序列,并要求您找到这些序列之和的最大值 例如,如果N被指定为5
2 4 13行描述了序列[0, 13, 13, 13, 0]
3 5 11行描述了序列[0, 0, 11, 11, 11] 如果那些是唯一的行,我们从两者的逐点和得到结果序列,即[0, 13, 24, 24, 11]

现在我们可以通过差异序列描述上述序列的另一种方式,即在索引i,我们将保持索引i处的元素与索引{{处的元素之间的差异1}},我们可以通过差分序列的运行总和得到原始序列。

在上述序列的情况下,差异序列是:
i-1所描述的序列[0, 13, 0, 0, -13] 2 3 13所描述的序列[0, 0, 11, 0, 0] 3 5 11表示序列的总和。

一个重要特性是序列总和的差异序列是差异序列的总和

因此,对于每一行,解决方案的作用是对差异序列求和(由于序列的性质,最多只需要2次操作),然后找出差异序列的运行总和所需的最大值,从而得到序列的元素,并保持该运行总数的最大值。

虽然我给出的示例只有2行,但同样的想法适用于任意数量的行。

我希望这能为解决方案背后的理念提供良好的直觉。

答案 2 :(得分:3)

这两个地方帮助我更清楚地理解了这个算法。 Prefix_sum_array_and_difference_array
Stack Overflow

如果您想要一个简单而直接的解释: 初始,数组为0 0 0 0 0 cpp after the first operation, 1 2 100 it will become seq1: 100 100 0 0 0 and after second 2 5 100 seq2: 0 100 100 100 100 and after 3 4 100 seq2: 0 0 100 100 0 但是当我们应用差异数组时 在每一步,我们都会得到

cpp diff seq of seq1: 100 0 -100 0 0 diff seq of seq2: 0 100 0 0 0 -100 diff seq of seq3: 0 0 100 0 -100

一个重要特性是序列总和的差异序列是差异序列的总和。

它会给我们, cpp 100 100 0 0 -100 -100(for clarity purpose only) 或者您可以将所有序列添加为 cpp seq1+seq2+seq3 = 100 200 200 200 100 然后找到差值seq或差值数组,即100 100 0 0 -100,然后找到前缀数组。

为什么我们忽略前100? 阅读关于差异数组和前缀和数组的第一篇文章!!!!

之后,做前缀sum cpp 100 200 200 200 100 0 忽略最后0作为我们考虑的最后一个索引仅用于清晰目的。

所以,这两个步骤都为我们找到差异数组:) cpp a[p]+=sum; if((q+1)<=N) a[q+1]-=sum;

答案 3 :(得分:2)

蛮力方法要求我们迭代数组并将值添加到a到b范围内的每个元素。

我们可以执行此操作,而不是每次循环,我们在索引a处添加值,然后从索引b + 1中减去该值。最后,我们像从左到右添加一个前缀数组一样。

所以实际上发生的是我们添加的值从a反映到b。而且,由于我们从b + 1中减去了相同的值,因此在为b编制索引后就不会反映出来。

这个算法在O(n + m)中运行。

如果您对解释有任何疑问,可以观看此视频,该视频很好地解释了算法,并通过在少数测试案例上空运行算法来帮助更好地理解算法。短短7分钟的视频:)。

链接:https://youtu.be/JtJKn_c9lB4

答案 4 :(得分:1)

以下代码在C ++中为我工作。我在线上获得了一些帮助,然后进行了编码。

long arrayManipulation(int n, vector<vector<int>> queries) {
  vector<long> resultVector(n);
  long maxVal=0, x=0, i;

  for(int i = 0; i<n ; i++)
  {
      resultVector[i]=0;
  }

  for(i=0; i<queries.size(); i++)
  {
      resultVector[(queries[i][0])-1] += queries[i][2];

      if((queries[i][1]) <= n)
      {
          resultVector[(queries[i][1])] -= queries[i][2];
      }
  }

  for(i=0; i <n; i++)
  {
      x+=resultVector[i];
      if(x>maxVal)
      {
          maxVal=x;
      }
  }

  return maxVal;
}

答案 5 :(得分:1)

Before solving this problem you must know Prefix Sum Array & Difference array. 

consider below case
a b k
1 5 3
4 8 7
6 9 1 

# if we calculate original array 'A' from this it will be
[3,3,3,10,10,8,8,8,1,0]

# now, lets find out the difference array 'D(A)'
[3,0,0,7,0,-2,0,0,-7,-1]

# follow below steps & calculate the array
A[a] += k
A[b+1] -= k , b+1 < len(A)
you will get [3,0,0,7,0,-2,0,0,-7,-1] which is D(A) itself.

# P(0, D(A)) = A. i.e. calculate prefix sum array of D(A). you will get the original array.
[3,3,3,10,10,8,8,8,1,0]

return max :)

答案 6 :(得分:0)

不是将k加到数组中从a到b的范围内的所有元素上,而是将差值数组累加

每当我们在数组的任何索引处添加任何内容并应用前缀求和算法时,都会将同一元素添加到每个元素,直到数组结尾。

ex- n = 5,m = 1,a = 2 b = 5 k = 5

    i     0.....1.....2.....3.....4.....5.....6   //take array of size N+2 to avoid index out of bound
  A[i]    0     0     0     0     0     0     0

将k = 5添加到a = 2

A [a] = A [a] + k //应该从此处添加k元素的起始索引类似于a [p] + = sum;

     i    0.....1.....2.....3.....4.....5.....6 
   A[i]   0     0     5     0     0     0     0

现在应用前缀和算法

     i    0.....1.....2.....3.....4.....5.....6 
  A[i]    0     0     5     5     5     5     5

因此您可以看到K = 5在应用前缀sum之后添加到所有元素,直到末尾,但是我们不必在末尾添加k。因此,要取消此效果,我们还必须在b + 1索引之后添加-K,以便仅在[a,b]范围内才会有K个元素的添加效果。

A [b + 1] = A [b] -k //删除先前在bth索引之后添加的k元素的影响。(与a [q + 1]-= sum;相同) 这就是为什么在初始数组中将-k与+ k一起添加。

    i    0.....1.....2.....3.....4.....5.....6 
  A[i]   0     0     5     0     0     0    -5

现在应用前缀和数组

    i    0.....1.....2.....3.....4.....5.....6 
  A[i]   0     0     5     5     5     5     0

您现在可以看到,将K = 5从a = 2添加到b = 5,这是预期的。 在这里,我们只为每个查询更新两个索引,因此复杂度将为O(1)。

现在在输入中应用相同的算法

         # 0.....1.....2.....3.....4.....5.....6    //taken array of size N+2 to avoid index out of bound
5 3      # 0     0     0     0     0     0     0
1 2 100  # 0    100    0   -100    0     0     0       
2 5 100  # 0    100   100  -100    0     0   -100
3 4 100  # 0    100   100    0     0  -100   -100

要计算最大前缀和,请在获取最大累积前缀的同时将差分数组累加到。

执行所有操作后,现在应用前缀求和数组

    i      0.....1.....2.....3.....4.....5.....6 
  A[i]     0     100   200  200   200   100    0

现在您可以遍历此数组以找到最大为200的数组。 遍历数组将花费O(N)时间,而更新每个查询的两个索引将花费O(1)*查询数量(m)

总体复杂度= O(N)+ O(M)                   = O(N + M)

这意味着=(10 ^ 7 + 10 ^ 5)小于10 ^ 8(每秒)

注意:如果要搜索 video tutorial ,则必须将其签出 here 以获得详细说明。< / p>

答案 7 :(得分:0)

它使用数组差异的概念。将值添加到给定范围(i,j,k)是一个新概念。 i和j指定范围,k是要添加的值。 如果您检查链接,它将很有帮助。 https://www.geeksforgeeks.org/difference-array-range-update-query-o1