伙计们我正在努力解决 "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;
任何帮助......为什么我得到&#34;因超时而终止&#34; ???
答案 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>