考纲要求
- 理解模拟法是按题目给出的规则一步步执行,程序忠实还原过程而不是直接套公式。
- 能用变量记录关键时刻的状态,例如位置、方向、计数与累加值。
- 能处理带时间推进的循环,例如每天、每轮、每次操作更新一次状态。
- 能判断模拟过程的终止条件,避免死循环或漏掉最后一步。
典型题型
- 按给定规则模拟若干轮操作,输出最终状态。
- 模拟走格子、排队、报数等过程,求特定时刻的结果。
- 给出规则与若干次操作,要求写出中间状态。
常见错误
- 循环次数多算或少算一次,例如「第 n 天」与「经过 n 天」没有分清。
- 状态更新顺序写错,用已被当前步骤修改的值去计算下一步。
- 忘了处理边界,例如走到最右端后下一步继续向右越界。
- 终止条件只判断一次,导致过程提前结束或永远结束不了。
深入理解
模拟法的本质是「把规则翻译成变量」
模拟法没有固定公式,做法是:先找出题目描述里会变化的状态,每个状态用变量或数组存起来,再按题目给出的顺序一步步更新,直到满足终止条件。状态找得对不对,决定了程序好不好写。
例如排队报数题,需要记录的状态有:每个人是否还在队列里(布尔数组)、当前报的是几、当前轮到谁。三个状态写清楚,循环体就是把「报数 → 判断是否出列 → 轮到下一个」翻译成代码。不要一上来就写循环,先用中文把每一步列出来,再逐条翻译,能省掉大部分返工。
顺序、终止条件、边界三处最容易错
模拟题的失分点集中在三处:
- 顺序:更新状态的先后会影响结果。例如「先报数再判断」和「先判断再报数」差一轮。
- 终止条件:问「前 3 个出列的人」就要在出列计数达到 3 时停止,不能等到整个队列空。
- 边界:围成一圈时,最后一个人之后要回到第一个,用
pos = (pos + 1) % n而不是pos++。
循环变量被跳过(已出列的人不应参与报数)也是常见问题,处理方式是循环里先跳过标记为已出列的位置,而不是在外部改循环变量。
模拟题拿分的实用策略
模拟题代码通常不长但分支多,最容易出现「样例过了、隐藏测试点错」。稳妥的做法是:先在纸上手算前 5 到 10 步,把每一步的状态写下来,再写代码;写完用同一组数据跑一遍,和手算结果逐步比对。如果中途发现状态对不上,问题一定出在最近修改的那个变量上。
另外要留意题目里的「重新开始」「忽略」「连续 k 次」这类词,它们对应额外的计数器,漏掉就会整体错位。模拟不考技巧,考的是把规则一条不漏地实现出来。
自编例题
模拟排队报数
n 个人围成一圈,从 1 开始报数,报到 3 的人出列,下一个人重新从 1 报数。模拟输出前 3 个出列人的编号。
queue<int> q;
for (int i = 1; i <= n; i++) q.push(i);
int cnt = 1, out = 0;
while (out < 3) {
int x = q.front(); q.pop();
if (cnt == 3) { cout << x << ' '; out++; cnt = 1; }
else { q.push(x); cnt++; }
}用队列模拟圆圈,队首的人是当前报数的人。报数到 3 就出列并重置计数,否则把它放回队尾并让计数加一。循环终止条件是要输出的三个人都已经出列。模拟类题目关键是把「谁在什么时刻做什么」写成明确的更新顺序,并且保证每一轮只改变一个状态,这样才不容易出错。如果更新顺序反了,比如先计数再判断,报数结果就会整体偏移。
相关真题
这块内容在整条路线里的位置:GESP C++ 3 级备考路径。
READY TO START
开始练习
先做一套本级别测评,报告会指出哪些知识点需要重点补;再回到本页对照考纲要求逐条核对,把不确定的条目记下来集中练。
常见问题
模拟法适合什么题?
适合规则描述清楚、过程可以逐步推进的题,例如排队、走格子、报数、日期推算,通常不需要复杂公式。
怎么避免死循环?
明确写出终止条件并在每轮检查,确认每轮至少改变一个会影响终止条件的量,必要时加计数器兜底。
模拟题的数据范围怎么看?
看总操作次数是否在可接受范围内。模拟通常按步数计复杂度,步数太大时要考虑找规律或数学方法。
