【问题标题】:competitive programming and input竞争性编程和输入
【发布时间】:2021-07-31 20:00:40
【问题描述】:


我正在为几周后将在我的教职员工中进行的竞争性锦标赛进行练习,因此我遇到了一个小问题。
比赛限制了 java.io 的使用。*(除了 IOException...)
我需要读取(从标准输入)输入,每个测试用例都用空行分隔。测试用例结束 - 找到 EOF 时。
我需要找到一种不使用 java.io 从 IO 获取数据的方法
到目前为止,我得到了这个(有效)——它返回一个包含每个测试用例的字符串,当我没有测试用例时返回 null。

public static String getInput() throws IOException {
    int curr=0;
    int prev=0;
    StringBuilder sb = new StringBuilder();
    while (true) {
        curr = System.in.read();
        if (curr == -1) { 
            return null; //end of data
        }
        if (curr == '\r') {
            curr = System.in.read();
        }
        if (curr == prev && curr == '\n') {
            return sb.toString(); //end of test case
        } //else:
        sb = sb.append((char)curr);
        prev = curr;
    }
}

性能(对于 IO)被忽略了,所以我不在乎我每次只读取一个字节。
问题:是否有更优雅(编码更短、更快)的方法来实现相同的目标?

【问题讨论】:

  • 如果您需要从标准输入读取而不是在程序中编写该代码,更好的方法是直接从终端使用 pwsh/cmd/bash 函数,例如this跨度>

标签: java io


【解决方案1】:

事实上,有几种方法可以在竞争性编程中处理 Java 中的输入。

方法一:使用java.util.Scanner

这是读取输入的最简单方法,使用起来也非常简单。如果您有大量输入,它可能会很慢。如果您的程序不断出现 TLE(超出时间限制),但您的程序具有正确的时间复杂度,请尝试使用第二种或第三种方法读取输入。

初始化Scanner sc = new Scanner(System.in);

读取整数:int n = sc.nextInt(); 方法二:使用java.io.BufferedReader

如果输入量很大,并且问题的时间限制很严格,请使用此选项。它确实需要更多的工作,包括按空格分割输入,或使用Integer.parseInt(str); 从输入中提取整数。

你可以在这里找到速度比较https://www.cpe.ku.ac.th/~jim/java-io.html

初始化:BufferedReader reader = new BufferedReader(System.in);

读取整数:int n = Integer.parseInt(reader.readLine());

方法 3:使用自定义阅读器直接从 FileDescriptor 读取

这种方法是 Java 中最快的方法。它确实需要很多工作,包括实现阅读器,以及在出现任何问题时进行调试。如果时间限制很严格并且允许您将代码带入比赛,请使用此方法。经测试,此方法比第二种方法快得多,但它通常不会为您提供优势,因为它的速度仅为 BufferedReader 方法的 2 倍左右。

这是我朋友写的这种方法的一种实现: https://github.com/jackyliao123/contest-programming/blob/master/Utils/FastScanner.java

阅读器的使用实际上取决于您对阅读器的实现。建议保留一份阅读器的副本,它在一定程度上可以保证工作,因为您在比赛中最不想要的就是拥有一个非功能阅读器并调试您的程序的其余部分,认为那里有一些错误。

希望这对您的比赛有所帮助并祝您好运!

【讨论】:

    【解决方案2】:

    您可以尝试以下方法并通过包装 System.in 来提高效率。

    public static String readLine() throws IOException {
        StringBuilder sb = new StringBuilder();
        for (int ch; (ch = System.in.read()) > 0;)
            if (ch == '\r') continue;
            else if (ch == '\n') break;
            else sb.append(ch);
        return sb.toString();
    }
    

    编辑:在 Oracle JVM 上,System.in 是一个 BufferedInputStream,它包含一个包含 FileDescriptor 的 FileInputStream。所有这些都在 java.io 中。

    【讨论】:

    • BufferedInputStream 和 InputStream 都是 java.io 。说明是具体的 - java.io 受到限制,因此我们将无法使用 InputStream
    • System.in 是一个 InputStream,所以我猜你也不能使用它。 ;) 该方法在没有缓冲的情况下仍然有效。
    【解决方案3】:

    如果允许java.util,您可以尝试使用java.util.Scanner 类。它有一些有用的方法可以根据需要读取一行、一个标记甚至是一个数字。但它比BufferedReader 慢,可能比直接使用System.in.read() 慢。

    由于System.in 实现了InputStream 接口,因此使用System.in.read(byte[] b) 读取输入也可能会加快一些速度。这样,您一次可以读取一堆字节,而不仅仅是一个字节,这应该会更快。但是在比赛期间必须编码和调试它所增加的复杂性可能不值得。

    编辑: 在网上搜索时,我发现有人在 UVa forum 中讨论使用 System.in.read(byte[] b),当时 UVa 对 Java 的支持很糟糕。

    【讨论】:

    • 要获取 Scanner 的实例,我需要一个 InputStream,这是被禁止的
    • 使用new Scanner (System.in);
    • 我很确定这也是被禁止的,但我会试试这个。如果我获得使用扫描仪的批准,我会接受这个答案。
    • @amit gr:要获得Scanner,您只需从java.util 导入。此外,您不必创建任何InputStream,只需使用您已有的InputStream 实例-System.in。因此,您将通过Scanner s=new Scanner(System.in); 进行实例化。如果你可以在别处使用System.in,我看不出有什么理由不能在这里使用。
    • 我会检查一下,如果可能的话会接受这个答案。无论如何感谢您的帮助:)
    【解决方案4】:

    您可以使用扫描仪

    import java.util.Scanner;//put this above the class
    Scanner scanner = new Scanner(System.in); //this creates the scanner
    int input = scanner.nextInt();
    

    .nextInt() 接受整数
    .nextLine() 接受字符串

    【讨论】:

      猜你喜欢
      • 2021-01-07
      • 2012-04-02
      • 1970-01-01
      • 2019-04-25
      • 1970-01-01
      • 2022-01-12
      • 2017-04-19
      • 2019-02-15
      • 1970-01-01
      相关资源
      最近更新 更多