ACM 模式:算法竞赛输入输出与快速解题
从 LeetCode 核心代码模式到 ACM 模式的过渡,是很多笔试新手的第一道坎。
LeetCode 给好了函数签名和测试用例调用,但笔试平台(牛客、赛码、HackerRank)通常要求你写完整的 main 函数,自己处理输入输出。本专题帮你快速上手 ACM 模式。
为什么需要 ACM 模式?
| 场景 | 模式 | 难点 |
|---|---|---|
| LeetCode | 核心代码模式 | 只需写函数体 |
| 大厂笔试 | ACM 模式 | 需自己读输入、写输出 |
| Codeforces/AtCoder | ACM 模式 | IO 量大,需要优化 |
| 软件设计师/蓝桥杯 | ACM 模式 | 有格式要求,需严格匹配 |
ACM 模式的核心能力:
- 快速解析输入格式
- 处理多组测试用例(while 循环读入)
- 输出格式精确匹配(空格、换行)
- 大数据量下的快速 IO
Python 输入输出模板
基础模板
import sys
def solve():
# 读取第一行:n 个数
n = int(sys.stdin.readline().strip())
# 读取第二行:n 个整数
arr = list(map(int, sys.stdin.readline().split()))
# 处理逻辑
result = sum(arr)
# 输出
print(result)
if __name__ == "__main__":
solve()
多组测试用例(T 组)
def solve():
T = int(sys.stdin.readline())
for _ in range(T):
n = int(sys.stdin.readline())
arr = list(map(int, sys.stdin.readline().split()))
# 处理...
print(result)
if __name__ == "__main__":
solve()
读到 EOF 为止(未知行数)
def solve():
for line in sys.stdin:
nums = list(map(int, line.split()))
if not nums:
continue
# 处理...
print(result)
if __name__ == "__main__":
solve()
快速 IO(处理 10^5 以上输入)
import sys
def fast_input():
"""一次性读取所有输入,适合大规模数据"""
data = sys.stdin.buffer.read().split()
it = iter(data)
n = int(next(it))
arr = [int(next(it)) for _ in range(n)]
return n, arr
def solve():
n, arr = fast_input()
# 处理...
sys.stdout.write(str(result))
if __name__ == "__main__":
solve()
sys.stdin.buffer.read() 比逐行 readline() 快 5-10 倍,大数据量必用。
Java 输入输出模板
基础模板
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
// 处理...
long result = solve(arr);
System.out.println(result);
}
static long solve(int[] arr) {
long sum = 0;
for (int x : arr) sum += x;
return sum;
}
}
快速 IO(BufferedReader + StringTokenizer)
import java.io.*;
import java.util.*;
public class Main {
static class FastReader {
BufferedReader br;
StringTokenizer st;
FastReader() {
br = new BufferedReader(new InputStreamReader(System.in));
}
String next() {
while (st == null || !st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() { return Integer.parseInt(next()); }
long nextLong() { return Long.parseLong(next()); }
double nextDouble() { return Double.parseDouble(next()); }
}
public static void main(String[] args) {
FastReader fr = new FastReader();
int n = fr.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) arr[i] = fr.nextInt();
long result = solve(arr);
System.out.println(result);
}
static long solve(int[] arr) {
long sum = 0;
for (int x : arr) sum += x;
return sum;
}
}
Scanner 在 10^5 数据量以上会非常慢,笔试中务必使用 BufferedReader。
快速输出(BufferedWriter)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(result + "\n");
bw.flush();
C++ 输入输出模板
基础模板
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> arr(n);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
long long sum = 0;
for (int x : arr) sum += x;
cout << sum << endl;
return 0;
}
关键优化
ios::sync_with_stdio(false); // 关闭 C 和 C++ 流的同步
cin.tie(nullptr); // 解除 cin 和 cout 的绑定
这两行代码让 cin/cout 接近 scanf/printf 的速度。
大数据量读入
// 如果需要极致速度,使用 scanf/printf
int n;
scanf("%d", &n);
vector<int> arr(n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
printf("%lld\n", result);
常见输入格式处理
矩阵输入
# n x m 矩阵
n, m = map(int, sys.stdin.readline().split())
matrix = []
for _ in range(n):
row = list(map(int, sys.stdin.readline().split()))
matrix.append(row)
图输入(边列表)
# n 个节点,m 条边
n, m = map(int, sys.stdin.readline().split())
graph = [[] for _ in range(n)]
for _ in range(m):
u, v, w = map(int, sys.stdin.readline().split())
graph[u].append((v, w))
字符串输入(含空格)
# 读取整行含空格的字符串
s = sys.stdin.readline().strip()
常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 末尾空格 | 输出末尾多了空格 | 用 ' '.join(map(str, arr)) |
| 多组数据换行 | 每组数据输出后空一行 | 控制换行位置 |
| 数据范围超限 | int 存不下 | Java 用 long,Python 自动大整数 |
| 浮点精度 | 小数输出要求 2 位 | System.out.printf("%.2f", val) |
| 空行读取 | 测试用例间有空行 | 用 while 跳过空行 |
| 大数据 TLE | 算法正确但 IO 慢 | 使用快速 IO 模板 |
笔试策略
时间分配(120 分钟 3 题为例)
| 阶段 | 时间 | 目标 |
|---|---|---|
| 读题 + 分析 | 10 分钟 | 理解所有题目,判断难度排序 |
| 第 1 题 | 25 分钟 | 简单题必拿下 |
| 第 2 题 | 40 分钟 | medium 难度,核心逻辑 |
| 第 3 题 | 35 分钟 | hard 难度,争取部分分 |
| 检查 + 提交 | 10 分钟 | 边界条件、样例验证 |
部分分策略
对于 hard 题,如果无法全 AC,可以尝试:
- 暴力解法:小数据量能过,拿 30-50% 分
- 特殊条件优化:如数据范围某个维度很小
- 贪心/近似:不一定最优,但能过大部分测试点
平台差异速查
| 平台 | 特点 | 注意 |
|---|---|---|
| 牛客 | 输入在控制台,可本地测试 | 类名必须是 Main(Java) |
| 赛码 | 类似牛客 | 注意多语言编译器版本 |
| HackerRank | 函数模板已给 | 函数内处理即可 |
| Codeforces | 大数据、高并发 | 必须用 fast IO |
| AtCoder | 时间限制较松 | Python 友好 |
练习建议
- 牛客网:“华为/字节/阿里笔试真题"专题
- Codeforces:Div 2 A/B 题练手,Div 2 C 题进阶
- AtCoder:Beginner Contest,适合入门
- 蓝桥杯:国内比赛,填空 + 编程混合
核心原则:ACM 模式不等于算法难,而是工程细节(输入输出、边界处理)决定成败。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。