STL 不是背函数名,而是根据需求选维护结构:去重计数、先进先出、动态最值、连续下标,各有固定工具。
L03 复杂度、STL 与模型复盘
从“这题用什么容器”开始,训练数据范围、容器复杂度、模型识别三件事。
一、先回答问题
用 P1271 看计数与排序,用 P1540 看队列模拟,用 P2678 只做二分答案识别。
二、本节知识点地图
| 知识点 | 学生要会什么 | 课堂检查 |
|---|---|---|
| STL 容器用途 | 会选 vector/queue/set/map/priority_queue | 容器选型题 |
| STL 复杂度 | 知道常用操作量级 | 口答复杂度 |
| 计数排序 | 知道编号范围小时能开桶 | P1271 手推 |
| 队列模拟 | 维护顺序和是否存在 | P1540 状态表 |
| 方向数组 | 统一遍历 8 个方向 | P2670 九宫格 |
| 二分答案识别 | 说清 check 和单调性 | P2678 口答 |
三、概念讲清楚
| 需求 | 容器 | 复杂度 |
|---|---|---|
| 连续下标 | vector | 访问 O(1) |
| 先进先出 | queue | 入队/出队 O(1) |
| 动态最值 | priority_queue | 插入/删除 O(log n) |
| 有序去重/计数 | set/map | O(log n) |
四、书籍例子:STL 不是背函数
读入文本,提取单词,去重并按字典序输出。`set<string>` 同时解决“去重”和“排序”,这就是容器选型的价值。
模拟题不难在算法,而难在边界。方向数组 `dx/dy` 可以把 8 个方向统一成一个循环,避免长串 if-else。
五、互动演示:容器选型
六、自己选答案
P1540 机器翻译最自然的维护结构是什么?
P2678 跳石头今天要先做到什么程度?
点击选项后显示反馈。
七、样例精讲:三道题串起来
| 题目 | 模型 | 课堂重点 | 作业要求 |
|---|---|---|---|
| P1271 | 计数 / 排序 | sort 和桶的取舍 | 蛋* 必做 |
| P1540 | 队列模拟 | queue 和 set 同步 | 全员必做 |
| P2678 | 二分答案 | 答案范围 + check | 丁* 限时写 |
八、二分答案从 P2678 入门
猜一个最短跳跃距离 x,从左到右扫描石头。如果当前石头和上一块保留石头的距离小于 x,就删掉当前石头。若删除数不超过 m,说明 x 可行。
单调性:x 越大,要求越严格,越不容易可行。
九、当堂练习与课后习题
| 类型 | 题目/任务 | 要求 |
|---|---|---|
| 当堂练习 | 容器选型 5 题 | 说出容器和复杂度 |
| 当堂练习 | P1540 容量=2 状态表 | queue/set 同步 |
| 蛋*作业 | P1271、P1540 | 每题写模型判断 |
| 丁*作业 | P1540、P2678、P1182 check 设计 | P2678 限时 30 分钟 |
十、课堂验收
下课前必须答出
1. set/map 分别适合什么? 2. priority_queue 为什么为 Dijkstra 铺垫? 3. P1540 为什么不能只用 queue? 4. 扫雷为什么适合方向数组? 5. P2678 的 check(x) 检查什么?
十一、C++14 片段
#include <bits/stdc++.h>
using namespace std;
priority_queue<int, vector<int>, greater<int>> pq;
map<int, int> cnt;
queue<int> q;
set<int> inMemory;
for (int x : words) {
if (!inMemory.count(x)) {
q.push(x);
inMemory.insert(x);
if ((int)q.size() > capacity) {
inMemory.erase(q.front());
q.pop();
}
}
}
读代码重点:P1540 的关键不是 queue 本身,而是 queue 和 set 必须同步维护。