例题给全套:题面、样例、参考代码、以及一步步的讲解。先自己读题想一遍, 再看代码,最后对着讲解核一遍自己想的对不对。
练习只给题面、样例和一句提示,答案是折叠起来的 —— 请先自己写一版跑通了再展开。写不出来也别直接看答案,先回速查那一组找找。
每份代码都能直接点运行:输入框里已经填好了样例输入, 把它改一改再跑,是这一页最值钱的用法。
先把地基摆一遍
这一组里每一条都被本书量过,不是传说。而要先分清两件常被混在一起的事:「读对」和「读快」。 读错了是 WA(比如 getline 吃到一个空行),读慢了是 TLE —— 两者的症状完全不同,救法也完全不同。
★ 先说「读快」这一半:什么时候要管它,是一道算术题。数一数这道题总共要读多少个数:10⁶ 个以内,默认 cin 就够(约 0.16 秒);到 10⁷ 个,就必须关同步(第 6 章那道题读 47 MB,不关同步会超时)。⇒ 不到这个量级就别折腾,写快读反而容易写错。
★ 再说「读对」:>> 会自动跳过空格和换行,所以读一串数字从来不用管格式。但 getline 不跳 —— 它从当前位置一直读到换行为止。两者混用时,前一个 >> 留下的那个换行就会被 getline 当成「一个空行」读走。这是本组最常见的坑。
例题(2 道)
每道都给全套:题面 → 样例 → 参考代码 → 一步步的讲解。
例题 ① 先读个数,再读整行
第一行给一个整数 n,接下来 n 行,每行是一句可能带空格的话。请对每一行输出两个数:这一行有多少个字符、以及有多少个单词。
「单词」指的是被空格分开的、非空的一段。行首行尾都可能有空格,中间也可能有连续多个空格。
n(1 ≤ n ≤ 100);接下来 n 行,每行一个字符串(可能含空格,长度 ≤ 1000)。n 行,每行两个整数:该行的长度、该行的单词数。输入
2 hello world a b c
输出
11 2 9 3
hello world 长 11、两个单词; a b c 长 9(行首两个空格也算字符)、三个单词。⚠ 这道题不能用 cin >> s —— 它遇到空格就停,读到的只是一个词。换成 getline 之后会撞上本组最有名的那个坑:先试着不加任何处理跑一遍,看第一行读到了什么。
// 例题 ①「先读个数,再读整行」—— 读入与输出组//// 本组最常见的坑就在这道题里:cin >> n 停在数字后面,// **那一行的换行还留在输入里** ⇒ 紧接着的 getline 会立刻撞上它,读到一个空串。// 改法是中间加一句 cin.ignore()(扔掉一个字符)。
#include <bits/stdc++.h>using namespace std;
int main() { int n; cin >> n; cin.ignore(); // ← 少了这一句,下面第一次 getline 读到的是空串
for (int t = 0; t < n; t++) { string line; getline(cin, line); // 一直读到换行,空格照收
int words = 0; for (int i = 0; i < (int)line.size(); i++) if (line[i] != ' ' && (i == 0 || line[i - 1] == ' ')) words++; // 「一个单词的开头」= 自己不是空格,而前一个是空格(或者自己就在行首)
cout << line.size() << ' ' << words << "\n"; } return 0;}点「运行 ▶」看结果
cin >> n读完那个数之后,停在数字的后面 —— 那一行的换行还留在输入里没人管。- 紧接着的
getline(cin, line)是「从当前位置一直读到换行」⇒ 它当场就撞上那个换行,于是读到一个空串,而且把那个换行吃掉了。⇒ 后面每一行都错位一格。 - 改法是中间加一句
cin.ignore();—— 扔掉一个字符(就是那个换行)。(另一种写法是先白读一次getline,效果一样。) - 数单词不用别的库:一个字符是「单词的开头」,当且仅当它自己不是空格,而它前面是空格(或者它就在行首)。按这句话写一个 if,一遍扫完。
- ⚠ 而
>>和getline的这条差别,反过来也是>>的好处:>>自动跳过空格和换行 ⇒ 全程只用>>时,输入的排版怎么折行都无所谓。
这一组最常见的 bug 就是这一个,而它的症状很有迷惑性:程序不崩、不报错,只是第一行读到了空的。
例题 ② 读到文件结尾
输入是若干个整数,但题目不告诉你有几个。请输出它们的个数、总和,以及平均值(保留两位小数)。
⚠ 如果一个数都没有,个数和总和都输出 0,平均值输出 0.00。
|aᵢ| ≤ 10⁹,个数 ≤ 10⁶),用空格或换行分隔,直到文件结尾。输入
3 1 4 1 5
输出
5 14 2.80
2.80。「不知道有几个」在 C++ 里有一个固定写法。⚠ 本地测试时要按 Ctrl+D(Linux)告诉程序输入结束,不然它会一直等。
// 例题 ②「读到文件结尾」—— 读入与输出组//// 题目不告诉你有几个数时用 while (cin >> x):// 「cin >> x」这个表达式本身有一个值 —— 读成功了是真,读到头了是假。// ⚠ 本地测试时要按 Ctrl+D(Linux)告诉它输入结束,不然它会一直等。
#include <bits/stdc++.h>using namespace std;
int main() { ios::sync_with_stdio(false); // 这两句放在 main 的第一行;放到读入之后就没用了 cin.tie(nullptr);
long long sum = 0; int cnt = 0, x; while (cin >> x) { sum += x; cnt++; }
cout << cnt << ' ' << sum << "\n";
// 保留两位小数:cout 要先说一句 fixed << setprecision(2),之后一直有效。 // ⚠ 上面关了同步,这里就**不要**再用 printf —— 两套输出混着用可能乱序。 cout << fixed << setprecision(2) << (cnt ? (double)sum / cnt : 0.0) << "\n"; return 0;}点「运行 ▶」看结果
while (cin >> x)——cin >> x这个表达式本身有一个值:读成功了是「真」,读到头(或者读到不是数字的东西)是「假」。⇒ 直接拿它当循环条件。- 开头那两句
ios::sync_with_stdio(false); cin.tie(nullptr);是关掉 cin 和 scanf 的同步,读入能快好几倍。⚠ 必须写在main的第一行 —— 写在读入之后就没用了。 - 什么时候需要它是一道算术题:数一数这道题总共要读多少个数。10⁶ 个以内默认
cin就够(约 0.16 秒);到 10⁷ 个就必须关同步。这道题上限正好 10⁶ ⇒ 写上它,反正代价为零。 - 保留两位小数:
cout << fixed << setprecision(2) << x,说一次之后一直有效。 - ⚠ 而既然关了同步,这里就不要再用
printf—— 两套输出混着用可能乱序。(要用printf的话,long long 的格式是%lld,两位小数是%.2f。)
第 22 章那道「导弹拦截」就是典型的「输入不给个数」。⇒ 而这一组真正要分清的是两件事:读错了是 WA,读慢了是 TLE,症状和救法完全不同。
用到的写法:while (cin >> x) ios::sync_with_stdio(false); cin.tie(nullptr); printf("%lld", x)
练习(3 道)
答案折叠着,先自己写一版。
练习 ① 行末不能留空格
给 n 个整数,请把它们倒序输出在一行里,两两之间恰好一个空格,行末不能有空格。
n(1 ≤ n ≤ 10⁵);第二行 n 个整数。n 个整数。输入
4 1 2 3 4
输出
4 3 2 1
1 2 3 4 倒过来是 4 3 2 1 —— 最后那个 1 后面什么都没有。最顺手的写法是 cout << a[i] << ' ';,而它正好会在行末多留一个空格。两条改法:要么「我不是第一个就先打个空格」,要么「我不是最后一个才打空格」。
这不是小题大做:第 47 章那道题上,「行末多一个空格」在 300 轮对拍里被抓了 284 次,而把每行末尾的空格去掉再比一次 ⇒ 整列变成 0。⇒ 肉眼看两份输出「明明一模一样」,所以对拍从来都是逐字节比。
用到的写法:'\n' 而不是 endl
练习 ② 每行先告诉你有几个数
第一行一个整数 n,接下来 n 行,每行先是一个整数 k,然后是 k 个整数。请对每一行输出这 k 个数的和。
n(1 ≤ n ≤ 1000);接下来 n 行,每行一个 k(1 ≤ k ≤ 1000)和 k 个整数(|aᵢ| ≤ 10⁹)。n 行,每行一个整数。输入
2 3 1 2 3 2 10 20
输出
6 30
这道题看着要「按行读」,其实完全不用管行。想想 >> 会不会跳过换行。
⇒ 一旦全程只用 >>,输入的排版就不是问题;只有 getline 才在乎行。
用到的写法:while (cin >> x)
练习 ③ 把整个输入倒着打出来
把输入的每一行原样保存下来,然后按相反的顺序逐行输出。
⚠ 行里可能有空格,行数事先不知道。
输入
first line second third
输出
third second first line
third、second、first line —— 注意最后那行里有空格。两件事各对应一个写法:「行里有空格」决定了用哪个读;「行数不知道」决定了循环条件怎么写。这两个写法能拼在一起。