ACM模式Java输入输出处理完全指南
难度: 基础到进阶
标签: ACM, Java, 输入输出, 算法竞赛, 编程技巧
适用场景: 算法竞赛、笔试面试、在线编程平台
📝 概述
ACM模式是算法竞赛中常见的编程模式,与LeetCode的函数式编程不同,ACM模式需要手动处理输入输出。本指南涵盖Java中各种常见的输入输出处理模式。
💭 基础知识
初步理解
ACM模式要求程序能够:
- 读取多组测试数据
- 处理不同格式的输入
- 按照指定格式输出结果
- 处理输入结束条件
常用类和方法
Scanner类:最常用的输入处理类
BufferedReader类:高效读取大量数据
StringTokenizer类:字符串分割
System.out.print()和System.out.println():输出
🎯 常见输入输出模式
模式一:固定数量输入
场景描述:
输入第一行是测试用例数量n,后面跟着n行数据。
输入样例:
输出样例:
代码实现(使用Scanner):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); for (int i = 0; i < n; i++) { int a = sc.nextInt(); int b = sc.nextInt(); System.out.println(a + b); } sc.close(); } }
|
模式二:直到特定值结束
场景描述:
输入多组数据,直到遇到特定值(如0)时结束。
输入样例:
输出样例:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNextInt()) { int a = sc.nextInt(); int b = sc.nextInt(); if (a == 0 && b == 0) { break; } System.out.println(a + b); } sc.close(); } }
|
更多测试用例:
-
立即结束:
输出:(无输出)
-
负数处理:
输出:
-
单组数据:
输出:
模式三:直到文件末尾
场景描述:
输入多组数据,直到文件末尾(EOF)。
输入样例:
输出样例:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNextInt()) { int a = sc.nextInt(); int b = sc.nextInt(); System.out.println(a + b); } sc.close(); } }
|
更多测试用例:
-
单行输入:
输出:
-
多行混合数据:
输出:
-
实际应用场景(文件重定向):
模式四:字符串处理
场景描述:
处理包含空格的字符串输入。
输入样例:
1 2 3 4
| 3 Hello world Java programming ACM algorithm competition Data structures and algorithms
|
输出样例:
1 2 3
| Word count: 4 Word count: 3 Word count: 4
|
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); sc.nextLine(); for (int i = 0; i < n; i++) { String line = sc.nextLine(); String[] words = line.split(" "); System.out.println("Word count: " + words.length); } sc.close(); } }
|
⚠️ 重要注意事项:Scanner的nextLine()陷阱
Q1: 为什么 nextLine() 在 nextInt() 或 next() 之后不生效?
nextInt() 或 next() 不会读取换行符,导致后续的 nextLine() 直接读取到空行。
解决方法:在 nextInt() 或 next() 后加一个 nextLine() 来消耗换行符。
错误示例:
1 2 3 4 5 6
| Scanner sc = new Scanner(System.in); System.out.print("输入数字:"); int num = sc.nextInt(); System.out.print("输入字符串:"); String str = sc.nextLine(); System.out.println("数字:" + num + ",字符串:" + str);
|
输入:
输出:
nextLine() 读取了 nextInt() 留下的换行符,导致 str 为空。
修正方法:
1 2 3 4 5 6 7
| Scanner sc = new Scanner(System.in); System.out.print("输入数字:"); int num = sc.nextInt(); sc.nextLine(); System.out.print("输入字符串:"); String str = sc.nextLine(); System.out.println("数字:" + num + ",字符串:" + str);
|
输出:
更多测试用例:
-
多空格处理(改进版本):
1 2
| String[] words = line.split("\\s+");
|
-
空字符串测试:
输出:
1 2
| Word count: 1 Word count: 2
|
-
特殊字符处理:
1 2 3
| 2 Hello, world! Java-programming@2025
|
输出:
1 2
| Word count: 2 Word count: 1
|
模式五:高性能输入(BufferedReader)
场景描述:
处理大量数据时,需要更高效的输入方式。
输入样例:
1 2 3 4 5
| 4 100 200 50 75 300 400 25 35
|
输出样例:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import java.io.*; import java.util.StringTokenizer;
public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); for (int i = 0; i < n; i++) { st = new StringTokenizer(br.readLine()); int a = Integer.parseInt(st.nextToken()); int b = Integer.parseInt(st.nextToken()); System.out.println(a + b); } br.close(); } }
|
性能对比测试:
- 大数据量测试(10万行):
1 2 3 4 5 6
| 100000 1 2 3 4 5 6 ... [继续99997行]
|
性能数据:
- Scanner: 约500-800ms
- BufferedReader: 约100-200ms
- 性能提升:4-5倍
适用场景:
- n > 10000时推荐使用
- 时间限制严格的竞赛题目
- 需要处理大规模输入数据
模式六:二维数组输入
场景描述:
输入二维数组或矩阵。
输入样例:
1 2 3 4
| 3 4 1 2 3 4 5 6 7 8 9 10 11 12
|
输出样例:
代码实现:
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
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int rows = sc.nextInt(); int cols = sc.nextInt(); int[][] matrix = new int[rows][cols]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { matrix[i][j] = sc.nextInt(); } } int sum = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { sum += matrix[i][j]; } } System.out.println("Matrix sum: " + sum); sc.close(); } }
|
动态数组实现(使用ArrayList):
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
| import java.util.ArrayList;
public class DynamicMatrix { public static void main(String[] args) { int rows = 2, cols = 3; ArrayList<ArrayList<Integer>> matrix = new ArrayList<>();
for (int i = 0; i < rows; ++i) { ArrayList<Integer> row = new ArrayList<>(); for (int j = 0; j < cols; ++j) { row.add(0); } matrix.add(row); }
matrix.get(0).set(0, 1); matrix.get(0).set(1, 2); matrix.get(0).set(2, 3); matrix.get(1).set(0, 4); matrix.get(1).set(1, 5); matrix.get(1).set(2, 6);
System.out.println(matrix.get(1).get(2));
for (int i = 0; i < matrix.size(); ++i) { for (int j = 0; j < matrix.get(i).size(); ++j) { System.out.print(matrix.get(i).get(j) + " "); } System.out.println(); } } }
|
更多测试用例:
-
正方形矩阵:
输出:
-
单行矩阵:
输出:
-
单列矩阵:
输出:
-
边界情况(1×1矩阵):
输出:
模式七:不定长数组输入
场景描述:
每行输入不定数量的数字。
输入样例:
1 2 3 4
| 1 2 3 4 5 10 20 30 40 100
|
输出样例:
代码实现:
方法1:使用 Scanner
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.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNextLine()) { String line = sc.nextLine(); if (line.trim().isEmpty()) { continue; } String[] numbers = line.split("\\s+"); int sum = 0; for (String num : numbers) { if (!num.trim().isEmpty()) { sum += Integer.parseInt(num); } } System.out.println(sum); } sc.close(); } }
|
方法2:使用 Scanner嵌套
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
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String line; long sum;
while (sc.hasNextLine()) { line = sc.nextLine(); if (line.trim().isEmpty()) { continue; }
Scanner lineScanner = new Scanner(line); sum = 0; while (lineScanner.hasNextLong()) { long num = lineScanner.nextLong(); sum += num; } System.out.println(sum); lineScanner.close(); } sc.close(); } }
|
更多测试用例:
-
多空格处理:
输出:
-
单行单数:
输出:
-
负数处理:
输出:
-
空行结束:
输出:
模式八:多组测试用例(每组不同格式)
场景描述:
复杂的输入格式,每组测试用例可能包含不同类型的数据。
输入样例:
1 2 3 4 5 6 7 8 9 10
| 3 5 1 2 3 4 5 sum 0 4 10 20 30 40 max 0 6 1 2 3 4 5 3 count 3
|
输出样例:
1 2 3
| Case #1: 15 Case #2: 40 Case #3: 2
|
代码实现:
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 41 42 43 44 45
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); for (int caseNum = 1; caseNum <= t; caseNum++) { int n = sc.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } String operation = sc.next(); int k = sc.nextInt(); int result = processArray(arr, operation, k); System.out.println("Case #" + caseNum + ": " + result); } sc.close(); } private static int processArray(int[] arr, String operation, int k) { switch (operation.toLowerCase()) { case "sum": return java.util.Arrays.stream(arr).sum(); case "max": return java.util.Arrays.stream(arr).max().orElse(0); case "count": return (int) java.util.Arrays.stream(arr).filter(x -> x == k).count(); case "find": for (int i = 0; i < arr.length; i++) { if (arr[i] == k) return i; } return -1; default: return 0; } } }
|
更多测试用例:
-
查找操作:
1 2 3 4 5 6 7
| 2 5 10 20 30 40 50 find 30 3 1 2 3 find 5
|
输出:
-
单元素数组:
输出:
-
边界测试:
输出:
🚀 关键要点
核心概念
- 输入流处理:理解如何读取不同类型的输入数据
- 循环控制:掌握各种输入结束条件的处理
- 字符串分割:熟练使用split()和StringTokenizer
- 异常处理:合理处理输入异常
常见陷阱
- 换行符处理:nextInt()后使用nextLine()需要特别注意
- 空格处理:字符串输入中的空格分割问题
- EOF判断:正确处理文件结束条件
- 性能问题:大量数据时选择合适的输入方式
性能优化建议
- 小数据量:使用Scanner,代码简洁
- 大数据量:使用BufferedReader + StringTokenizer
- 超大数组:考虑使用快速输入输出类
📚 实战练习
练习题1:A + B Problem
描述:计算两个数的和,直到文件结束。
输入:多行,每行两个整数
输出:每行输出对应的和
练习题2:字符串统计
描述:统计输入字符串中各个字符的出现次数。
输入:第一行是测试用例数量,后面是字符串
输出:每个字符串的字符统计结果
练习题3:矩阵运算
描述:实现矩阵的转置操作。
输入:矩阵的行数和列数,然后是矩阵元素
输出:转置后的矩阵
🏷️ 相关标签
- ACM
- Java
- 输入输出
- 算法竞赛
- 编程技巧
- 笔试面试
📖 参考资料
最后更新:2025-01-15