ACM赛制java输入输出

主要在leetcode上以核心代码模式刷题如果突然切换到ACM模式会很不习惯, 但是这也是在面试过程中可能会考察的

因此掌握ACM赛制java的基本输入输出操作十分必要

ScannerSystem.out.print 在最开始会工作得很好,但是在处理更大的输入的时候会降低效率,因此我们会需要使用一些方法来提高 IO 速度。

  • 总计 Java 算法的 ACM 模式需要注意的地方

输入输出模板

使用 Kattio + StringTokenizer 作为输入

最常用的方法之一是使用来自 Kattis 的 Kattio.java 来提高 IO 效率。

  • 这个方法会将 StringTokenizerPrintWriter 包装在一个类中方便使用。

而在具体进行解题的时候(假如赛会/组织方允许)可以直接使用这个模板。下方即为应包含在代码中的 IO 模板,由于 Kattis 的原 Kattio 包含一些并不常用的功能,下方的模板经过了一些调整(原 Kattio 使用 MIT 作为协议)。

而下方代码简单展示了 Kattio 的使用:

Kattio.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Kattio extends PrintWriter {
private BufferedReader r;
private StringTokenizer st;
// 标准 IO
public Kattio() { this(System.in, System.out); }
public Kattio(InputStream i, OutputStream o) {
super(o);
r = new BufferedReader(new InputStreamReader(i));
}
// 文件 IO
public Kattio(String intput, String output) throws IOException {
super(output);
r = new BufferedReader(new FileReader(intput));
}
// 在没有其他输入时返回 null
public String next() {
try {
while (st == null || !st.hasMoreTokens())
st = new StringTokenizer(r.readLine());
return st.nextToken();
} catch (Exception e) {}
return null;
}
public int nextInt() { return Integer.parseInt(next()); }
public double nextDouble() { return Double.parseDouble(next()); }
public long nextLong() { return Long.parseLong(next()); }
}

简单使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test {
public static void main(String[] args) {
Kattio io = new Kattio();
// 字符串输入
String str = io.next();
// int 输入
int num = io.nextInt();
// 输出
io.println("Result");
// 请确保关闭 IO 流以确保输出被正确写入
io.close();
}
}

使用 StreamTokenizer 作为输入

在某些情况使用 StringTokenizer 会导致 ==MLE==(Memory Limit Exceeded,超过内存上限),

此时我们需要使用 StreamTokenizer 作为输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.*;
public class Main {
// IO 代码
public static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in), 32768));
public static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
public static double nextDouble() throws IOException { in.nextToken(); return in.nval; }
public static float nextFloat() throws IOException { in.nextToken(); return (float)in.nval; }
public static int nextInt() throws IOException { in.nextToken(); return (int)in.nval; }
public static String next() throws IOException { return in.sval; }
public static long nextLong() throws Exception { in.nextToken(); return (long)in.nval;}

// 使用示例
public static void main(String[] args) throws Exception {
int n = nextInt();
out.println(n);
out.close();
}
}

两者对比

Kattio + StringTokenizer 的方法与 StreamTokenizer 的方法之间的分析与对比

  1. StreamTokenizer相较于StringTokenizer使用的内存较少,当 Java 标程 MLE 时可以尝试使用StreamTokenizer,但是StreamTokenizer会丢失精度,读入部分数据时会出现问题;
    • StreamTokenizer 源码存在 Type,该 Type 根据你输入内容来决定类型,倘若你输入类似于 123oi数字开头 的字符串,他会强制认为你的类型是 double 类型,因此在读入中以 double 类型去读 String 类型便会抛出异常;
    • StreamTokenizer 在读入 1e14 以上大小的数字会丢失精度;
  2. 在使用 PrintWriter 情况下,需注意在程序结束最后 close() 关闭输出流或在需要输出的时候使用 flush() 清除缓冲区,否则内容将不会被写入到控制台/文件中。
  3. Kattio 是继承自 PrintWriter 类,自身对象具有了 PrintWriter 的功能,因此可以直接调用 PrintWriter 类的函数输出,同时将 StringTokenizer 作为了自身的成员变量来修改。而第二种 Main 是同时将 StreamTokenizerPrintWriter 作为了自身的成员变量,因此在使用上有些许差距。

综上所述,在大部分情况下,StringTokenizer 的使用处境要优越于 StreamTokenizer,在极端 MLE 的情况下可以尝试 StreamTokenizer,同时 int 范围以上的数据 StreamTokenizer 处理是无能为力的。

结论 : 优先使用 Kattio

题目示例

P1001 A+B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

输入两个整数 a, b,输出它们的和image-20220824201421908

java语言程序范例

1
2
3
4
5
6
7
8
9
import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) throws Exception {
Scanner cin=new Scanner(System.in);
int a = cin.nextInt(), b = cin.nextInt();
System.out.println(a+b);
}
}

本题java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.*;
import java.util.StringTokenizer;

public class Main {
public static void main(String[] args) throws Exception{
Kattio io = new Kattio();
// 输入
long a=io.nextLong();
long b=io.nextLong();
io.print(a+b); // 输出
io.close(); // 请确保关闭 IO 流以确保输出被正确写入
}
}
class Kattio extends PrintWriter {
private BufferedReader r;
private StringTokenizer st;
// 标准 IO
public Kattio() { this(System.in, System.out); }
public Kattio(InputStream i, OutputStream o) {
super(o);
r = new BufferedReader(new InputStreamReader(i));
}
// 文件 IO
public Kattio(String intput, String output) throws IOException {
super(output);
r = new BufferedReader(new FileReader(intput));
}
// 在没有其他输入时返回 null
public String next() {
try {
while (st == null || !st.hasMoreTokens())
st = new StringTokenizer(r.readLine());
return st.nextToken();
} catch (Exception e) {}
return null;
}
public int nextInt() { return Integer.parseInt(next()); }
public double nextDouble() { return Double.parseDouble(next()); }
public long nextLong() { return Long.parseLong(next()); }
}

注意点

  1. 类名称必须采用public class Main方式命名

  2. 在提交java代码的时候, 不要带上package

  3. 多行数据处理

    1
    2
    3
    4
    static Scanner in = new Scanner(System.in);  
    while(in.hasNextInt())
    //or
    while(in.hasNext())

参考网站