GESP C++ 3 级 · 知识点

算法:模拟法

GESP C++ 三级要求能把题目描述的过程逐步翻译成程序,用变量记录状态并按步骤推进。模拟法不需要巧妙的公式,考的是严谨:规则一条不漏、顺序一步不错、终止条件明确。

对应知识域:算法:模拟法相关真题 4 套

本页为本站原创整理,例题与解析均由本站编写,不取自任何题库原题;考试范围以 CCF 官方公布为准,本平台不是 GESP 官方平台。

考纲要求

  • 理解模拟法是按题目给出的规则一步步执行,程序忠实还原过程而不是直接套公式。
  • 能用变量记录关键时刻的状态,例如位置、方向、计数与累加值。
  • 能处理带时间推进的循环,例如每天、每轮、每次操作更新一次状态。
  • 能判断模拟过程的终止条件,避免死循环或漏掉最后一步。

典型题型

  • 按给定规则模拟若干轮操作,输出最终状态。
  • 模拟走格子、排队、报数等过程,求特定时刻的结果。
  • 给出规则与若干次操作,要求写出中间状态。

常见错误

  • 循环次数多算或少算一次,例如「第 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

开始练习

先做一套本级别测评,报告会指出哪些知识点需要重点补;再回到本页对照考纲要求逐条核对,把不确定的条目记下来集中练。

常见问题

模拟法适合什么题?

适合规则描述清楚、过程可以逐步推进的题,例如排队、走格子、报数、日期推算,通常不需要复杂公式。

怎么避免死循环?

明确写出终止条件并在每轮检查,确认每轮至少改变一个会影响终止条件的量,必要时加计数器兜底。

模拟题的数据范围怎么看?

看总操作次数是否在可接受范围内。模拟通常按步数计复杂度,步数太大时要考虑找规律或数学方法。

本级别其他知识点

官方依据