PAT/PTA刷题笔记:口罩发放题的5个关键解题技巧与常见错误复盘
最近在准备PAT/PTA考试的同学,一定对这类逻辑模拟题又爱又恨。题目描述往往长达数百字,规则复杂得像现实世界的业务流程,稍有不慎就会掉进出题人精心设计的陷阱。口罩发放题就是这类问题的典型代表——它融合了多重条件判断、自定义排序、数据结构应用等核心考点。今天我们就用这道题作为案例,拆解5个关键解题技巧,并复盘那些让人抓狂的常见错误。
1. 复杂题目规则的快速拆解方法论
面对题干超过500字的题目,很多同学的第一反应是"从哪开始读?"。实际上,这类管理系统的模拟题通常包含三个核心模块:
- 输入规则:数据的分块结构、字段含义、约束条件
- 处理规则:业务逻辑的条件分支(如本题的发放条件)
- 输出规则:结果的排序要求、去重规则、格式规范
针对口罩发放题,我们可以用表格快速梳理关键规则:
| 规则类型 | 具体要求 | 对应代码实现 |
|---|---|---|
| 身份证校验 | 必须18位纯数字 | 字符串长度检查+isdigit()遍历 |
| 发放间隔 | 成功领取后P天内不能再领 | 用map记录最后发放日期 |
| 发放顺序 | 先按提交时间排序,同时间按出现顺序 | 自定义sort比较函数 |
| 特殊输出 | 身体不适人员需去重并按首次出现顺序输出 | 额外维护一个vis映射表 |
提示:在草稿纸上画出这样的规则映射表,能帮助你在编码时快速定位各个条件的实现位置,避免遗漏关键约束。
2. 双重排序的比较函数实现技巧
题目要求发放时"按提交时间排序,时间相同则按出现顺序",这需要自定义排序函数。常见错误是直接比较字符串形式的时间"hh:mm",这会导致"09:10" < "9:30"的错误结果。正确的做法是:
struct Applicant { string name, id; int hour, minute; int seq; // 记录原始出现顺序 }; bool compare(const Applicant &a, const Applicant &b) { if(a.hour != b.hour) return a.hour < b.hour; if(a.minute != b.minute) return a.minute < b.minute; return a.seq < b.seq; // 时间相同时按出现顺序 }几个易错细节:
- 时间比较要拆分为hour和minute两个整数字段
- seq字段需要在输入时立即记录,不能依赖后续数组下标
- 比较函数要声明为const引用避免拷贝开销
3. 状态记录的map使用陷阱
题目需要跟踪每个身份证的最近发放日期,很多同学直接用map<string, int>存储。但在多天数据处理时容易忽略两个问题:
- 跨天数据隔离:每天的申请列表是独立的,但发放记录是累积的
- 无效数据污染:不合法的身份证不应进入发放记录
改进方案是采用分层记录策略:
map<string, int> lastDay; // 记录合法身份证的最后发放日 set<string> dailyValidIDs; // 当天有效的身份证集合 void processDay(int day) { dailyValidIDs.clear(); // 先筛选合法身份证 for(auto &app : applicants) { if(isValidID(app.id)) { dailyValidIDs.insert(app.id); } } // 然后处理发放逻辑 for(auto &app : sortedApplicants) { if(dailyValidIDs.count(app.id) && (lastDay[app.id] + P < day || lastDay[app.id] == 0)) { // 发放逻辑 lastDay[app.id] = day; } } }4. 身份证校验的优化写法
题目要求身份证必须是18位数字,新手常写出冗长的校验代码。其实可以用STL算法简化:
bool validateID(const string &id) { return id.length() == 18 && all_of(id.begin(), id.end(), ::isdigit); }注意避免的陷阱:
- 前导零是允许的(如"001234..."是合法ID)
- 校验要在所有处理前进行,避免无效数据污染状态
- 在C++中直接使用isdigit可能需注意locale问题,安全起见使用::isdigit
5. 多天数据处理的变量初始化
这类多批次数据处理题最易犯的错误就是变量作用域混乱。典型错误案例:
for(int day = 1; day <= D; day++) { vector<Applicant> applicants; // 每天重新初始化 int T, S; cin >> T >> S; // 处理逻辑... }看似合理,但会丢失跨天需要持续跟踪的信息(如发放记录)。正确的做法是:
区分生命周期不同的变量:
- 跨天持续的:发放记录map、身体不适人员集合
- 当天有效的:申请列表、排序结果
每日必须重置的容器:
vector<Applicant> dailyApps; // 每日申请记录 for(int day = 1; day <= D; day++) { dailyApps.clear(); // 复用容器避免反复分配内存 // 读取当天数据到dailyApps }注意边界条件:
- 第一天处理时lastDay应为空
- P=0时的特殊处理(虽然题目限定P≥1)
- 最后一天结束后才输出身体不适人员
这道题看似简单,但想要在考试环境下快速准确地实现,需要对这些细节点有充分准备。建议在练习时故意制造这些错误场景,观察错误表现,这样在真实考试时就能快速识别类似问题。