由于黑客等级超时而终止"数组操作"

时间:2017-12-23 15:07:46

标签: java

伙计们我正在努力解决 "Arrays Manipulation" in hacker rank

您将获得一个大小的列表(1索引),用零初始化。您必须对列表执行操作并输出列表中所有元素的最大值。对于每个操作,您将获得三个整数,并且您必须为从索引到(包括两者)的所有元素添加值。

例如,考虑一个大小列表。初始列表将是= [,,],并且在执行update =之后,新列表将是= [,,]。在这里,我们为索引2和3之间的元素增加了值30.注意列表的索引从1开始。

我的解决方案是



import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Solution {

    public static void main(String[] args) {
       Scanner in = new Scanner(System.in);
         int n = in.nextInt();
         List<Long> arr = new ArrayList<Long>(Collections.nCopies(n, new Long(0)));
         int m = in.nextInt();
         for(int a0 = 0; a0 < m; a0++){
             int a = in.nextInt();
             int b = in.nextInt();
             int k = in.nextInt();
             
             for(int i=a-1;i<=b-1;i++){
            	  arr.set(i, arr.get(i)+k);
             }
         }
         in.close();
         System.out.println(Collections.max(arr));
    }
}
&#13;
&#13;
&#13;

任何帮助......为什么我得到&#34;因超时而终止&#34; ???

2 个答案:

答案 0 :(得分:2)

最优线性算法

我说实话,我自己没有提出这个想法,我首先阅读了一些让我在线性时间内找出最佳算法的东西。基本上,这个想法是在索引+k处记录值a,在每个查询处记录索引-k处的值b。然后,它允许您通过简单计算数组的运行总和(从左侧开始)来计算m查询后每个单元格的总值。非常聪明,我很难过,这不是我自己的想法!

这是Scala的一个解决方案,我通过了所有HackerRank的测试:

object Solution {
    def main(args: Array[String]) {
        val lines       = scala.io.Source.stdin.getLines()
        val Array(n, m) = lines.next().split(' ').map(_.toInt)

        val arr = Array.ofDim[Int](n)

        for (line <- lines) {
            val Array(low, high, k) = line.split(' ').map(_.toInt)
            arr(low  - 1) += k
            if (high < arr.length) arr(high) -= k
        }

        var runningSum = BigInt(0)
        var max = BigInt(0)

        for (i <- arr) {
            runningSum += i
            max = runningSum.max(max)
        }

        println(max)
    }
}

参考的其他想法

我最初考虑使用可能在这里有用的数据结构。它被称为interval tree。使用此数据结构,您可以以对数时间存储和查询一组间隔。

我的想法是,如果您设法将更新查询存储为与值相关联的间隔(要添加到间隔的每个元素的数量),您可以在O(m log n + m)中解决问题,这已经很多了比原始O(m * n)解决方案更快,但仍然没有上述线性解法快。

答案 1 :(得分:0)

约束-

3≤≤10^ 7

1≤≤2⋅10^ 5

1≤≤≤

0≤≤10^ 9

为什么您的暴力解决方案不起作用?

今天的生成系统可以在一秒钟内执行10 ^ 8操作。请记住,在最坏的情况下,每个查询必须处理N = 10 ^ 7个输入。因此,如果您使用O(NM)复杂度的解决方案,则必须处理(10 ^ 7 * 10 ^ 5)= 10 ^ 12运算(在更坏的情况下(根本无法在1秒内计算出))

这就是为什么您会因蛮力解决方案而遇到超时错误的原因。 因此,您需要优化代码,这可以借助前缀求和数组来完成。

不是将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元素的位置开始索引

     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 //删除第b个索引之后先前添加的k元素的影响。 这就是为什么在初始数组中将-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(每秒)

以下是代码:

static long arrayManipulation(int n, int[][] queries) {

        long outputArray[] = new long[n + 2];
        for (int i = 0; i < queries.length; i++) {
            int a = queries[i][0];
            int b = queries[i][1];
            int k = queries[i][2];
            outputArray[a] += k;
            outputArray[b+1] -= k;
        }
        long max = getMax(outputArray);
        return max;
    }

    /**
     * @param inputArray
     * @return
     */
    private static long getMax(long[] inputArray) {
        long max = Long.MIN_VALUE;
        long sum = 0;
        for (int i = 0; i < inputArray.length; i++) {
            sum += inputArray[i];
            max = Math.max(max, sum);
        }
        return max;

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