跳到主要内容

模拟、递推与边界

本节目标

把题意翻译成状态、操作顺序与边界条件,并用循环不变量检查实现。

基础实现题看起来是在考语法,真正要做的是把自然语言的题意转换为可执行的状态、明确的操作顺序和可检查的边界。先把这些约定写清楚,代码才会短而可靠。

识别信号

出现“按规则逐个处理”“对每一位做相同操作”“下一项由前面的结果得到”时,通常不需要寻找复杂技巧:先考虑规则模拟、数位与进位,或从已知状态递推。

“模拟”不是照抄题意,而是先确定状态、规则优先级和更新顺序。同一轮中若既要读取旧状态又要写入新状态,就必须说明读到的是哪一个版本;规则有多个分支时,也要先写出它们的优先级。

通用建模流程

  1. 写下状态:循环中哪些变量或数组位置代表当前结果。
  2. 把题目规则拆成一次操作,并确定各规则的判断优先级。
  3. 确定更新顺序:本轮读取旧状态后再写入,还是可以原地更新。
  4. 用一个最小例子手推首轮、中间轮和最后一轮。
  5. 最后单独检查输入为空、循环是否进入,以及答案应从哪个状态返回。

循环不变量与边界清单

循环不变量是每轮都不变的事实。循环开始前,要能说清已处理部分是什么、未处理部分是什么;每轮结束后,要能说清新处理的一个元素怎样让这个事实继续成立。例如,处理数位时,已扫描的后缀已经是正确结果;构造行时,已写出的元素已经满足本行的递推关系。

提交前逐项检查:

  • 空输入是否应该直接返回空结果或零值;
  • 单元素是否仍满足初始化和返回位置;
  • 首尾位置会不会访问不存在的前驱或后继;
  • 全进位会不会需要新增最高位;
  • 第一层初始化是否已经给出递推所需的基例。

三类基础模型

规则模拟

遍历输入或计数范围,按已写明的优先级选择输出或操作。规则重叠时,先处理更具体的情形,避免较宽泛的条件提前截走分支。

数位与进位

从最低位向高位处理,把进位作为状态的一部分。每轮先合并当前数位和进位,再写回当前位并更新下一轮进位;循环结束后还要检查是否残留进位。

从已知状态递推

先给出第一层或第一项,再说明新状态依赖哪些已经确定的状态。若依赖左右相邻位置,边界位置需要单独按定义处理,不能套用中间位置的公式。

母题序列

必学顺序完成下列四题;这里用于练习框架与检查方法,不展开它们的完整解法:

  1. Fizz Buzz:练习规则优先级和逐项输出。
  2. 回文数:练习首尾指针或数位状态的终止边界。
  3. 加一:练习从末位开始的进位,以及全进位新增最高位。
  4. 杨辉三角:练习第一层初始化与从已知行递推下一行。

常见误区

  • 直接把题目句子翻成条件分支,却没有定义每个变量保存的是旧值还是新值。
  • 让首尾位置进入只适用于中间位置的访问逻辑。
  • 只在普通样例中检查进位,遗漏所有数位都变化的情况。
  • 没有初始化基例,就开始使用递推式。
  • 根据最终输出倒推循环正确性,而没有在每轮结束后检查不变量。

迁移方向

以后遇到更长的模拟题、二维表格构造或简单动态规划时,仍可先问同样的问题:状态是什么?规则按什么顺序发生?循环开始前与结束后各自已知什么?哪几个边界需要独立定义?能先回答这些问题,就能把基础题中的可靠实现迁移到更复杂的模型。