相信我们已经非常熟悉 LeetCode 那种「站内文章核心代码」的写题模式,在这种模式下,我们可以更专注于代码逻辑本身。然而一些公司或学校的机考仍然使用 ACM 标准文件输入输出模式,在这种模式下需要对题目输入输出的逻辑进行额外的处理。

不同公司/机构的笔试模式

仅记录个人经验

年份 公司 环节 代码模式 代码联想 正确性检验 其它补充 是否允许本地 IDE
2024 字节 手撕 ACM 模式 自行检验 类似「掘金」的刷题平台,有代码联想和预先示例,有编辑器错误提示。 询问面试官
2025 美团 (忘了) 面试端的编码页面。有 main 函数和核心函数框架。
2023 USTC 机试 核心代码模式 记得时刻在剪切板保存好自己的代码,不要误触按钮导致一键清空。
2025 华为 ACM 模式 自行检验/用例通过状态 Java 只给 main 函数,部分包要自己引用。Scanner 自己写但有提示。类似 iLearning 平台。

9 月中旬机试系统改版,需安装时习之 APP。APP 对电脑系统软件会进行严格管控。
网易互娱、美团 牛客机试系统。非双机位。
小米集团 使用赛马在线考试客户端。代码页为空,但有输入输出提示。
小红书、京东 使用赛马在线考试网页版。
美团、拼多多 牛客机试系统。使用双机位。

Main 类的写法

像牛客、华为等机构机试都会给出最基础的代码模板。一些 OJ 中的代码编辑器不会给出任何内容(洛谷、Codeforces),这就需要我们手写 Main 类。注意,这个 Main 类是有固定格式的,不能随便改动,否则将报运行时错误。

1
2
3
4
5
6
7
8
9
10
11
import java.util.Scanner;
// 【注意】Main 类的写法不能出错
public class Main {
// Exception 可视情况 throws
public static void main(String args[]) throws Exception {
Scanner in=new Scanner(System.in);
int a = in.nextInt(), b = in.nextInt();
System.out.println(a+b);
}
}
// 【注意】别忘了在输入框中输入测试用例

小提示:对于第一次进陌生的 OJ,可以先写题库的第一道题「A+B」熟悉机试形式。一般题面或题解中附有不同语言的正确的写法。

输入与输出

输出写法 sout

最最基本的输出写法。

1
System.out.println("Hello Nowcoder!"); // 换行输出 IDEA 下快捷输入:sout

输入写法 java.util.Scanner

java.util.Scanner 是 Java 5 的新特征,我们可以通过 Scanner 类来获取用户的输入。这种输出写法比较简单,但在极端场景下容易超时。

1
2
3
import java.util.Scanner;
// 创建 Scanner 对象的基本语法
Scanner in = new Scanner(System.in);

华为机试中,需要手写包的导入以及 Scanner 的创建。

输入时用到类 Scanner 类,里面有几个常用方法:

  • hasNext():用于判断是否还有输入的数据,常放在 while 循环中判断输入是否结束。
  • next():读到空格就停止读取。适合读取单个字符或字符串。返回类型 String
  • nextLine():读取一整行数据,碰到换行则停止。返回类型 String

next()nextLine() 区别:

函数 next() nextLine()
是否阻塞 一定要读取到有效字符后才可以结束输入
结束符 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。 以 Enter 为结束符,该返回的是输入回车之前的所有字符。
带空格字符串 🟥 🟩

如果要输入 intfloat 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取。

  • hasNextInt() / nextInt():遇到空格时会停止读取,返回的结果为空格前读取到的部分。返回类型 int
  • hasNextDouble() / nextDouble()
next()nextLine() 连用时的注意事项

next()nextInt() 读取数据后指针还在当前行,如果紧跟 nextLine(),读取数据会出错。处理方法是:增多一个 nextLine()「吃掉」换行符即可。具体案例详看下文案例「单组字符串输入输出」。

如果全程只使用 nextInt() 不会有上述问题。

全是数字的话,使劲 nextInt() 就行。

输入输出写法 StreamTokenizer+PrintWriter

如果你在各大 OJ 中出现部分样例时间超限(Time Limit Exceed,TLE),尝试换成以下输入输出写法。

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
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException {
// 输入
int n = nextInt();
long m = nextLong();
f(m,n);
// 输出
os.println(m);

os.flush();
}

static StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter os = new PrintWriter(System.out);

static int nextInt() throws IOException {
sc.nextToken();
return (int) sc.nval;
}

static long nextLong() throws IOException {
sc.nextToken();
return (long) sc.nval;
}

输入形式

形式 A:单组/多组 + EOF/零尾模式

输入结束模式:

  • EOF 结束模式:输入有多组,文件末尾即输入结束。
  • 零尾模式:输入有多组,输入参数均为 0 时即输入结束。

给定两个整数 a 和 b ,请你求出 a+b 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
************************* EOF模式 ***************************
输入:
1 2
114 514
2024 727

输出:
3
628
2751

************************* 零尾模式 ***************************
输入:
1 2
114 514
2024 727
0 0

输出:
3
628
2751
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();

/*
零尾模式只需在此处增加以下语句即可
if(a==0 && b==0)break;
*/

System.out.println(a + b);
}
}
}

形式 B:T 组

给定两个整数 a 和 b ,请你求出 a+b 的值。

首先会给出一个数字 T,然后给出 T 组数字。

1
2
3
4
5
6
7
8
9
10
输入:
3
1 2
114 514
2024 727

输出:
3
628
2751
1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int T = in.nextInt();

while (T--!=0) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
System.out.println(a + b);
}
}

形式 C:单组字符串的输出与输出

这里将探讨 nextInt()nextLine() 混用的问题。

给定一个长度为 n 的字符串 s ,请你将其倒置,然后输出。

1
2
3
4
5
6
输入:
5
abcde

输出:
edcba
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

int len = Integer.valueOf(in.nextLine());
String str = in.nextLine();

char[] res = new char[len];
for(int i=0;i<len;i++){
res[len-1-i]=str.charAt(i);
}

System.out.println(new String(res));
}

注意到,上面的参考答案都统一使用了 nextLine() 函数读取输入。那么我们能不能用 nextInt()nextLine() 混合读取呢?答案是可以的,但是容易出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

int len = in.nextInt();
/** 位置 A **/
String str = in.nextLine();

System.out.println("读取到的len:"+len);
System.out.println("读取到的str:"+str);
}
/*
输出:
读取到的len:5
读取到的str:
*/

我们发现 str 并没有读取成功,这是因为第一次在读取 nextInt() 时,指针还在当前行,第二个 nextLine() 只读取到一个换行符。解决方法是在读取 nextInt() 后,在上面代码中「位置 A」处使用 nextLine()「吃掉」第一行的换行符即可。

输出形式:字符串格式化 String.format

给定一个小数 n ,请你保留 3 位小数后输出。
如果原来的小数位数少于 3 ,需要补充 0 。
如果原来的小数位数多于 3 ,需要四舍五入到 3 位。

看到这题目时我还纳闷,以前在 LeetCode 刷题就好少这种返回指定精度数组并要求保留小数点后的 0 的,其实仔细想想以前刷题的核心代码模式要么返回 intdouble,不需要我们处理输出的位数或补充 0。而这 ACM 模式输出的是 String,对输出自然会有另外的格式化要求。

1
2
3
4
5
输入:
1.23

输出:
1.230
1
2
3
4
5
6
7
8
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextDouble()) { // 注意 while 处理多个 case
double a = in.nextDouble();
System.out.println(String.format("%.3f", a).toString());
}
}

String.format 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能。

对浮点数进行格式化:占位符格式为 %[index$][标识]*[最小宽度][.精度]转换符

可用标识 可用转换符
- -,在最小宽度内左对齐,不可以与 0 标识一起使用。
- 0,若内容长度不足最小宽度,则在左边用 0 来填充。
- #,对 8 进制和 16 进制,8 进制前添加一个 0,16 进制前添加 0x
- +,结果总包含一个 +- 号。
- 空格,正数前加空格,负数前加 - 号。
- ,,只用与十进制,每 3 位数字间用 , 分隔。
- (,若结果为负数,则用括号括住,且不显示符号。
- b,布尔类型,只要实参为非 false 的布尔类型,均格式化为字符串 true,否则为字符串 false
- n,平台独立的换行符, 也可通过 System.getProperty("line.separator") 获取。
- f,浮点数型(十进制)。显示 9 位有效数字,且会进行四舍五入。如 99.99
- a,浮点数型(十六进制)。
- e,指数类型。如 9.38e+5
- g,浮点数型(比 %f%a 长度短些,显示 6 位有效数字,且会进行四舍五入)

更多示例:

本文参考