1. ACM模式与核心代码模式的区别
很多习惯了LeetCode刷题的同学第一次接触ACM模式时都会有点懵。这两种模式最大的区别在于:核心代码模式只需要关注算法逻辑,而ACM模式需要自己处理输入输出。
举个例子,LeetCode上的题目通常会给你一个预设好的函数接口,比如:
class Solution { public int maxProfit(int[] prices) { // 只需要写这部分逻辑 } }但在牛客、赛码等平台的ACM模式中,你需要自己写完整的程序:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] prices = new int[n]; for (int i = 0; i < n; i++) { prices[i] = sc.nextInt(); } // 然后才能开始写算法逻辑 } }我刚开始参加笔试时就踩过坑,花了15分钟才搞明白怎么读取输入数据,结果题目都没做完。后来发现很多大厂笔试都偏爱ACM模式,主要考察完整的问题解决能力。
2. Java处理输入的常见套路
2.1 基础输入方法
Java中最常用的输入工具是Scanner类,几个关键方法需要牢记:
Scanner sc = new Scanner(System.in); int a = sc.nextInt(); // 读取整数 double b = sc.nextDouble();// 读取双精度浮点数 String s = sc.next(); // 读取单词(遇到空格停止) String line = sc.nextLine();// 读取整行特别注意一个坑:混合使用nextInt()和nextLine()时,需要在nextInt()后加一个额外的nextLine()消耗换行符:
int n = sc.nextInt(); sc.nextLine(); // 必须加这行! String s = sc.nextLine();2.2 多组测试数据场景
笔试中最常见的输入格式是多组测试数据,通常有三种处理方式:
- 明确给出数据组数:
int T = sc.nextInt(); while (T-- > 0) { int a = sc.nextInt(); int b = sc.nextInt(); // 处理逻辑 }- 直到特定结束标志(比如0 0):
while (true) { int a = sc.nextInt(); int b = sc.nextInt(); if (a == 0 && b == 0) break; // 处理逻辑 }- 不确定组数(直到EOF):
while (sc.hasNextInt()) { int a = sc.nextInt(); int b = sc.nextInt(); // 处理逻辑 }3. 高频输入场景实战模板
3.1 数字处理模板
场景1:多组空格分隔的两个正整数
while (sc.hasNextInt()) { int a = sc.nextInt(); int b = sc.nextInt(); System.out.println(a + b); }场景2:第一行组数,后面每组两个数
int T = sc.nextInt(); while (T-- > 0) { int a = sc.nextInt(); int b = sc.nextInt(); System.out.println(a * b); }场景3:每行第一个数字表示后面数字个数
while (sc.hasNextInt()) { int n = sc.nextInt(); if (n == 0) break; int sum = 0; for (int i = 0; i < n; i++) { sum += sc.nextInt(); } System.out.println(sum); }3.2 字符串处理模板
字符串处理最容易踩坑,关键要分清next()和nextLine()的区别:
// 错误写法: int n = sc.nextInt(); String s = sc.next(); // 如果输入"3\nhello world",这里s会读到空字符串 // 正确写法: int n = sc.nextInt(); sc.nextLine(); // 消耗换行符 String s = sc.nextLine();多行字符串处理示例:
int n = sc.nextInt(); sc.nextLine(); // 别忘了! String[] arr = new String[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextLine(); }4. 输出格式化技巧
好的输出格式能避免被判题系统误判,常用方法:
// 保留两位小数 double d = 3.1415926; System.out.printf("%.2f\n", d); // 输出3.14 // 左对齐输出 System.out.printf("%-10s\n", "hello"); // "hello " // 数字补零 System.out.printf("%04d\n", 42); // "0042"复杂格式输出建议使用String.format():
String output = String.format("Case %d: %s %.2f", 1, "result", 3.14); System.out.println(output);5. 完整ACM模式模板
下面这个模板覆盖了90%的笔试场景,建议收藏:
import java.util.*; import java.io.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 读取数字 int n = sc.nextInt(); int[] nums = new int[n]; for (int i = 0; i < n; i++) { nums[i] = sc.nextInt(); } // 读取字符串(注意换行) sc.nextLine(); String str = sc.nextLine(); // 处理逻辑(这里以两数之和为例) int target = sc.nextInt(); int[] res = twoSum(nums, target); // 输出结果 System.out.println(res[0] + " " + res[1]); } private static int[] twoSum(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[]{map.get(complement), i}; } map.put(nums[i], i); } throw new IllegalArgumentException("No solution"); } }6. 时间管理策略
笔试中最怕的就是卡在IO处理上。我的经验是:
- 前5分钟:快速浏览所有题目,评估难度
- 模板准备:先把输入输出模板写好
- 60/40原则:简单题60分钟内做完,留40分钟给难题
- 最后10分钟:检查边界情况和输出格式
遇到不会的题目不要慌,先把输入输出框架写好,至少能拿部分分数。比如动态规划题可以先写输入代码,再写个暴力解法。
7. 调试技巧
ACM模式没法用IDE调试,可以:
- 打印中间变量:
System.err.println("Debug: a=" + a); // 用err输出不会被判题系统捕获- 使用测试用例:
// 本地测试时可以替换输入源 String testInput = "3\n1 2 3\nhello"; Scanner sc = new Scanner(new ByteArrayInputStream(testInput.getBytes()));- 边界测试:特别检查n=0, n=1等特殊情况
记住:笔试时即使答案不对,正确的IO处理也能给面试官留下好印象。平时可以多在牛客网的题库里练习ACM模式的题目,熟能生巧。