编程语言
首页 > 编程语言> > java – 在不到2秒的时间内从控制台读取600,000输入

java – 在不到2秒的时间内从控制台读取600,000输入

作者:互联网

目标

我正在解决this问题:

Little Girl and Maximum Sum

The little girl loves the problems on array queries very much.

One day she came across a rather well-known problem: you’ve got an
array of n elements (the elements of the array are indexed starting
from 1); also, there are q queries, each one is defined by a pair of
integers li, ri (1 ≤ li ≤ ri ≤ n). You need to find for each query the
sum of elements of the array with indexes from li to ri, inclusive.

The little girl found the problem rather boring. She decided to
reorder the array elements before replying to the queries in a way
that makes the sum of query replies maximum possible. Your task is to
find the value of this maximum sum.

Input The first line contains two space-separated integers n
(1 ≤ n ≤ 2·105) and q (1 ≤ q ≤ 2·105) — the number of elements in the
array and the number of queries, correspondingly.

The next line contains n space-separated integers ai (1 ≤ ai ≤ 2·105)
— the array elements.

Each of the following q lines contains two space-separated integers li
and ri (1 ≤ li ≤ ri ≤ n) — the i-th query.

Output In a single line print a single integer — the maximum sum of
query replies after the array elements are reordered.

使用测试7(请参阅问题末尾的测试结果),输入是一个大小为200,000且包含200,000个查询(具有r和l值)的数组.

输入看起来像这样:

200000 200000
189622 189286 194361 184457 182376 183471 197548 184736 195806 ... 200,000 integers

188738 290041
33738 90041
122738 390041
... 200,000 line

你可以download a sample input file,或者你可以创建自己的样本输入.数字本身并不重要.

问题

我需要读取600,000条输入线,而不超过2秒的执行时间.问题是,它甚至没有在2秒内读取前200,000输入.

如何在2秒内加速我的代码读取所有600,000输入?

代码

这是我的第一次尝试:

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int q = scanner.nextInt();
        int[] array = new int[n];
        for (int i=0; i<n; i++) {
            array[i] = scanner.nextInt();
        }
        int[][] qArray = new int[q][2];
        for (int i=0; i<q; i++) {
            qArray[i][0] = scanner.nextInt();
            qArray[i][1] = scanner.nextInt();
        }

        int[] index = new int[n];
        Arrays.sort(array);
        for (int i=0; i<q; i++) {
            for (int j = qArray[i][0]-1; j<qArray[i][1]; j++) {
                index[j]++;
            }
        }
        Arrays.sort(index);
        long sum =0;
        for (int i = 0; i<n; i++) {
            sum += index[i]*array[i];
        }
        System.out.println(sum);
    }
}

这是我的第二次尝试:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            String input = bufferedReader.readLine();
            String[] SplitInput = input.split(" ");
            int n = Integer.parseInt(SplitInput[0]);
            int q = Integer.parseInt(SplitInput[1]);

            String input2 = bufferedReader.readLine();

            int[][] qArray = new int[q][2];
            for (int i=0; i<q; i++) {
                input = bufferedReader.readLine();
                SplitInput = input.split(" ");
                qArray[i][0] = Integer.parseInt(SplitInput[0]);
                qArray[i][1] = Integer.parseInt(SplitInput[1]);
            }

            String[] SplitInput2 = input2.split(" ");
            int[] array = new int[n];
            for(int i=0; i<n; i++){
                array[i] = Integer.parseInt(SplitInput2[i]);
            }

            int[] index = new int[n];
            Arrays.sort(array);
            for (int i=0; i<q; i++) {
                for (int j = qArray[i][0]-1; j<qArray[i][1]; j++) {
                    index[j]++;
                }
            }
            Arrays.sort(index);
            long sum = 0;
            for (int i=0; i<n; i++) {
                sum += index[i]*array[i];
            }
            System.out.println(sum);
        }
        catch (NumberFormatException ex) {
            System.out.println("Not a number !");
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

检测结果

尝试1

7
Time: 2000 ms, memory: 20612 KB
Verdict: TIME_LIMIT_EXCEEDED

尝试2

7
Time: 2000 ms, memory: 41340 KB
Verdict: TIME_LIMIT_EXCEEDED

您可以查看我的完整测试结果herehere.同样,问题出在测试7上.

解决方法:

免责声明,我说我能够帮助你,我,但我无法为你解决.我无法在2秒内限制它,因为我没有正确理解问题本身.从技术上讲,我理解你的算法是做什么的,但是我有一些问题需要从概念上理解它,这让我无法找到大的优化.我发现了很大的优化.见答案的底部.

备注:我已经在测试页面上看到了较小测试的结果,并且绝对没有理由说您的第一次测试持续200毫秒.我只是不明白.它们在我的计算机上一直运行不到2毫秒(使用Java内部System.nanotime()方法).我相信测试包括JVM的启动.如果确实如此,我可以建议您切换到更优化的语言,如C或C++吗?这意味着测试在某种程度上违背了解释性语言.

算法

第一个问题是你的算法本身.它很慢:你实际上正在迭代200,000×x整数(平均值很高,来自你的文件).在最坏的情况下,您将迭代200,000×200,000 = 40,000,000,000英镑.难怪你的时间大约是20秒.

这太过分了.理想情况下,您应该能够像使用地图一样使用优化来减少双循环.你有大量的内存(256 MB),使用它.你已经做到了;做得更多.

最大的优化在于此处.我相信,不是通过索引递增索引,而是应该通过跳过此索引机制并使用更好的索引机制来找到另一个优化.我相信这就是为什么存在这个问题的原因:找到那个算法而不是其他算法.我不喜欢这样,但我不判断它.

读数据

我测试通过输入读取数据,你的方法很慢.我责备你使用Scanner.

我建议你使用这种结构和这种在<我的计算机上有100毫秒的总测试文件(我坚持认为......我的电脑不是你的电脑而我们的电脑不是你的测试评估的计算机).我相信它可以进一步减少,但我会说它已经足够了.这不是我们正在寻找的重大优化,但我相信它是第二个.

try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
  int[] counts = split(reader.readLine(), new int[2]);
  int n = c[0], q = c[1];
  int[] array = split(reader.readLine(), new int[n]);
  int[][] queries = new int[q][]; // Note, no size in the second part of the array creation.
  for (int i = 0; i < q; i++) {
    queries[i] = split(reader.readLine(), new int[2]);
  }
  ...
}

使用针对您的用例优化的适当拆分方法:

static int[] split(String s, int[] a) {
  int n = 0, aIndex = 0;
  for (int sIndex = 0, sLength = s.length(); sIndex < sLength; sIndex++) {
    char c = s.charAt(sIndex);
    if (c == ' ') { // Separator
      a[aIndex++] = n;
      n = 0;
    } else if ('0' <= c && c <= '9') { // Number
      n = n * 10 + (c - '0'); // Add a digit to the current number
    }
  }
  a[aIndex] = n;
  return a;
}

小优化

从概念上讲,您有以下代码:

for (int i = 0; i < q; i++) {
  // Fill qArray
}

for (int i = 0; i < q; i++) {
  // Work with index.
}

这两个循环可以合并,甚至可以消除对qArray的需求.您读取数据,然后直接处理它.如果循环彼此相邻,那么这并不重要,但是在您第一次尝试中对数组中的东西进行排序之间,并且您在第二次尝试中对数组进行排序并解析输入.这使得您的数据一方面远离CPU缓存,但您在另一方面处理I / O.我不知道哪一个更好.

您的代码中存在错误

我试图重新思考这个问题并找到了解决方案,但你的答案与我的答案完全不同.我实际上在你的代码中发现了一个错误.我的文件无法获得与您相同的结果.

在你的最后一个循环中,sum-loop,你将所有东西存储在一个long中,但它实际上可以获得一个int溢出.所以你应该这样做你的总和:

sum += (long)(index[i]) * array[i];

找到了!

关于你的代码,正如我所说,你有一个问题,因为你可能会得到超过400亿条指令.我可以用你在下面看到的内容来展平你的解决方案.我现在一直达到500毫秒.

public static void main(String[] args) throws IOException {
  long nanos = System.nanoTime();
  myMain();
  nanos = System.nanoTime() - nanos;
  System.out.printf("Time: %sms%n", MILLISECONDS.convert(nanos, NANOSECONDS));
}

static void myMain() throws IOException {
  try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
    int[] counts = split(reader.readLine(), new int[2]);
    int n = counts[0], q = counts[1];
    int[] array = split(reader.readLine(), new int[n]);
    int[] indices = new int[n];
    for (int i = 0; i < q; i++) {
      int[] query = split(reader.readLine(), new int[2]);
      indices[query[0] - 1]++;
      if (query[1] < n) {
        indices[query[1]]--;
      }
    }
    for (int i = 1; i < n; i++) {
      indices[i] += indices[i - 1];
    }
    sort(array, 200_000);
    sort(indices, 200_000);
    long sum = 0;
    for (int i = 0; i < n; i++) {
      sum += (long)array[i] * indices[i];
    }
    System.out.println(sum);
  }
}

static void sort(int[] array, int n) {
  int[] counts = new int[n+1];
  for (int element: array) {
    counts[element]++;
  }
  int current = 0;
  for (int i = 0; i < counts.length; i++) {
    Arrays.fill(array, current, current + counts[i], i);
    current += counts[i];
  }
}

static int[] split(String s, int[] a) {
  int n = 0, aIndex = 0;
  for (int sIndex = 0, sLength = s.length(); sIndex < sLength; sIndex++) {
    char c = s.charAt(sIndex);
    if (c == ' ') {
      a[aIndex++] = n;
      n = 0;
    } else if ('0' <= c && c <= '9') {
      n = n * 10 + (c - '0');
    }
  }
  a[aIndex] = n;
  return a;
}

请享用!

如果您对此优化有任何疑问,请不要犹豫;)

标签:java,time,execution-time,performance
来源: https://codeday.me/bug/20190527/1167129.html