例题给全套:题面、样例、参考代码、以及一步步的讲解。先自己读题想一遍, 再看代码,最后对着讲解核一遍自己想的对不对。
练习只给题面、样例和一句提示,答案是折叠起来的 —— 请先自己写一版跑通了再展开。写不出来也别直接看答案,先回速查那一组找找。
每份代码都能直接点运行:输入框里已经填好了样例输入, 把它改一改再跑,是这一页最值钱的用法。
先把地基摆一遍
这一组里没有一样是「算法」,全是写代码的手势。每一条都只要看一眼就会,但没人告诉你的话,读别人的代码时就会卡在那儿。
例题(2 道)
每道都给全套:题面 → 样例 → 参考代码 → 一步步的讲解。
例题 ① 温度记录
给 n 天的气温(可能是负数),请输出三个数:最高气温、最低气温、以及相邻两天温差绝对值的最大值。
然后再输出一行 n 个字符:第 i 个字符在第 i 天气温 ≥ 0 时是 +,否则是 -。
n(1 ≤ n ≤ 10⁵);第二行 n 个整数 tᵢ(|tᵢ| ≤ 10⁴)。n 个字符,中间不要有空格。输入
5 -3 0 7 2 -8
输出
7 -8 10 -+++-
-+++-。一口气用上四样:max、min、abs、三目。⚠ 只有一个地方要小心:n = 1 时「相邻两天」一对都没有。
// 例题 ①「温度记录」—— 零碎但天天用的那几样//// 一口气用上四样:max / min / abs / 三目。// 它们单独讲都不值一节,可本书每一份代码里都有它们。
#include <bits/stdc++.h>using namespace std;
int main() { int n; cin >> n; vector<int> t(n); for (int i = 0; i < n; i++) cin >> t[i];
int hi = t[0], lo = t[0], gap = 0; for (int i = 0; i < n; i++) { hi = max(hi, t[i]); // ⚠ max 的两个参数类型必须一样 lo = min(lo, t[i]); if (i > 0) gap = max(gap, abs(t[i] - t[i - 1])); // abs:绝对值 } cout << hi << ' ' << lo << ' ' << gap << "\n";
// 三目运算符:一行的 if-else,而且它是一个**有值的表达式**, // 所以能直接写在 cout << 里 —— ⚠ 但一定要加括号,它的优先级很低。 for (int i = 0; i < n; i++) cout << (t[i] >= 0 ? '+' : '-'); cout << "\n"; return 0;}点「运行 ▶」看结果
max(a, b)/min(a, b)只收两个参数;三个以上要加一对花括号:max({a, b, c})。- ⚠ 它们最常撞的编译错误是两个参数类型必须完全一样:
max(1, 2LL)编译不过,要写max(1LL, 2LL)或者max<long long>(1, 2)。 abs(x)是绝对值。整数用abs,浮点数写fabs更清楚。- 三目运算符
cond ? a : b读成「如果 cond 成立就取 a,否则取 b」。它是一个有值的表达式 ⇒ 可以直接写在cout <<里、写在return后面。 - ⚠ 而写进
cout <<时必须加括号:cout << (t >= 0 ? '+' : '-')。它的优先级很低,不加括号要么编译报错,要么结果不对。 n = 1的处理:让温差的初值是 0,并且只在i > 0时才去算差 ——这样一对都没有时答案自然是 0。
这四样单独讲都不值一节,可它们在本书每一份代码里都出现。
例题 ② 把最大的那个换到最前面
给 n 个整数,找出其中最大的那个(如果有多个并列,取下标最小的那个),把它和第一个数交换位置,然后输出整个数组。
n(1 ≤ n ≤ 10⁶);第二行 n 个整数。n 个整数,用空格隔开,行末不留空格。输入
5 3 9 4 9 1
输出
9 3 4 9 1
注意 n 可以到 10⁶。⚠ 这个规模下,数组写在 main 里面会出事 —— 想想它放在哪儿。另外:找最大值时要记的是值还是下标?
// 例题 ②「把最大的那个换到最前面」—— 零碎但天天用的那几样//// 这一页是竞赛代码最常见的开头长什么样:// 万能头 + using namespace std + const int N + 一个全局数组。// ⚠ 数组开成**全局**的两个理由:它自动全是 0,而且不占栈(几十万个元素写在 main 里会爆栈)。// ⚠ 那个多出来的 5 是留余量的:下标从 1 开始要多一格,加哨兵还要几格。
#include <bits/stdc++.h>using namespace std;
const int N = 100005; // 题面写 n ≤ 100000,就写 100005int a[N]; // 全局数组:程序启动时自动全填 0
int main() { int n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i];
int best = 0; // 记下标,不是记值 for (int i = 1; i < n; i++) if (a[i] > a[best]) best = i; // 用 > 而不是 >= ⇒ 并列时留住**下标最小**的那个
swap(a[0], a[best]); // 交换两个变量,不用自己写中间变量
for (int i = 0; i < n; i++) cout << a[i] << (i + 1 == n ? '\n' : ' '); return 0;}点「运行 ▶」看结果
- 竞赛代码最常见的开头就长这样:万能头
#include <bits/stdc++.h>、using namespace std;、const int N = 1000005;、然后一个全局数组int a[N];。 - ⚠ 数组开成全局的两个理由:① 它自动全是 0;② 它不占栈 —— 写在
main里的数组放在栈上(一般只有 8 MB),10⁶ 个 int 就是 4 MB,再多一点就直接段错误。 - ⚠ 那个多出来的 5 是留余量的:下标从 1 开始要多一格,加哨兵还要几格。题面写
n ≤ 1000000,就写1000005—— 不差这几个字节。 - 找最大值时记下标而不是记值:因为待会儿要交换位置,光有值没用。⚠ 而判断写
a[i] > a[best](不是>=)⇒ 并列时留住的是先出现的那个。 swap(a[0], a[best])一句就换完了,不用自己写中间变量。它对 vector、string 这些也管用,而且是 O(1) 的(只换内部指针,不真的搬数据)。
「> 还是 >=」这半个字符决定了并列时留谁 —— 而题面往往就在这半个字符上做文章。
用到的写法:const int N = 100005; swap(a, b) #include <bits/stdc++.h> using namespace std;
练习(3 道)
答案折叠着,先自己写一版。
练习 ① 三个数的最大值和最小值
给三个整数,输出其中最大的和最小的。
|a| ≤ 10¹⁸)。输入
3 9 -4
输出
9 -4
3 9 -4 里最大 9、最小 −4。max 只收两个参数 —— 三个数怎么办?(速查里那一条的最后半句。)⚠ 还有:注意数据范围。
用到的写法:max(a, b) min(a, b)
练习 ② 首尾对称交换
给 n 个整数,把第 1 个和第 n 个交换、第 2 个和第 n−1 个交换……然后输出。(也就是把整个数组倒过来,但请手写这个交换过程,别直接调 reverse。)
n(1 ≤ n ≤ 10⁵);第二行 n 个整数。n 个整数,用空格隔开,行末不留空格。输入
5 1 2 3 4 5
输出
5 4 3 2 1
1 2 3 4 5 倒过来是 5 4 3 2 1。⚠ 循环该走到哪儿停?走满全程会发生什么?(在纸上拿 4 个数走一遍就明白了 —— 这是最经典的一个「白忙」。)
走半程(i < n / 2)才对。n 是奇数时正中间那个不用动,而 n / 2 恰好把它跳过了。
用到的写法:swap(a, b)
练习 ③ 求最小值
给 n 个整数,输出其中最小的那个。
n(1 ≤ n ≤ 10⁵);第二行 n 个整数(|aᵢ| ≤ 10⁹)。输入
4 -3 -9 -1 -7
输出
-9
「不断取更小的那个」要先有一个初值。⚠ 数可能是负的 ⇒ 初值写 0 就错了。有一个现成的常量可以用。
⚠ 但如果后面要拿这个初值做加法(比如最短路里的 dist[u] + w),就不能用 INT_MAX —— INT_MAX + 1 当场溢出成负数,那时候要改用 0x3f3f3f3f(它自己加自己还不溢出)。