考纲要求
- 理解枚举法是把所有可能的候选答案逐一列出并检查的过程。
- 能确定合理的枚举范围,保证不漏掉正确答案且不做无意义的检查。
- 能用循环与条件判断实现简单枚举,例如找满足条件的整数、百钱百鸡这类问题。
- 能估算枚举法的执行次数,判断在给定数据范围下是否会超时。
典型题型
- 用枚举法解决给定范围的条件筛选问题。
- 根据时间复杂度判断枚举范围应如何缩小。
- 补全枚举程序中的循环边界或判断条件。
常见错误
- 枚举范围少了一端,例如漏掉边界值,导致答案缺失。
- 没有利用条件减少内层循环,导致执行次数远超预期。
- 判断条件写反,把不满足的当成满足。
- 多个变量之间存在约束关系时仍分别枚举,重复计算浪费步骤。
深入理解
枚举法就三件事:范围、条件、遍历
枚举(穷举)的思路是:把所有可能的答案逐一列出来,逐个检查是否符合条件。写程序时只需要答三个问题:枚举对象是什么、范围从哪里到哪里、判断条件怎么写。
例如「找出所有满足 x + y = 100、x 是 3 的倍数、y 是 5 的倍数的正整数对」,枚举对象是 x(y 可由 100 - x 推出),范围是 1 到 99,条件是 y > 0 && x % 3 == 0 && y % 5 == 0。只枚举一个变量、另一个用等式推出来,是枚举法最重要的优化习惯。
正确性靠「不漏」,效率靠「不重」
枚举法只要不漏掉任何一个可能,结果一定正确——这是它最大的优点,也是它比其他算法更好写的理由。但枚举要遍历整个范围,范围大时会超时,所以有三条常见的加速手段:
- 缩小范围:能由等式推出的变量就不枚举,例如上面的 y。
- 改变起点与步长:只枚举 3 的倍数,可以写
for (x = 3; x < 100; x += 3),直接少算三分之二。 - 提前跳出:一旦找到答案且题目只要求一个,立刻
break。
注意「不重」不是正确性要求,而是效率要求;重复枚举只是慢,漏枚举才会错。判断题问「枚举法会不会漏解」,答案是只有在范围划错时才会漏。
枚举与判素、判完全数的关系
很多经典问题是枚举的特例:判断质数就是枚举 2 到 √n 看有没有因子;找完全数就是枚举因子并对因子求和。把这类题写成统一模板:
for (int i = 2; i <= n; i++) {
int s = 0;
for (int j = 2; j * j <= i; j++)
if (i % j == 0) { s += j; if (j != i / j) s += i / j; }
}内层条件用 j * j <= i 而不是 j <= i,这是把 O(n) 降到 O(√n) 的最简单优化,也是枚举题能不能拿到满分的分界。
自编例题
找满足条件的整数对
找出所有满足 x + y 等于 100、x 是 3 的倍数、y 是 5 的倍数的正整数对。
for (int x = 3; x < 100; x += 3) {
int y = 100 - x;
if (y > 0 && y % 5 == 0) cout << x << ' ' << y << endl;
}题目有两个条件,x 与 y 由等式绑定,因此只需要枚举 x,y 直接算出来,省去一层循环。x 从 3 开始且每次加 3,保证枚举到的 x 都是 3 的倍数;再用 y 是 5 的倍数筛选。这样循环次数约为 33 次,比同时枚举 x 和 y 的 100 乘 100 次少得多。枚举法的优化通常就是这两步:缩小范围、利用约束消掉一层循环。
相关真题
这块内容在整条路线里的位置:GESP C++ 3 级备考路径。
READY TO START
开始练习
先做一套本级别测评,报告会指出哪些知识点需要重点补;再回到本页对照考纲要求逐条核对,把不确定的条目记下来集中练。
常见问题
枚举法什么时候会超时?
当枚举次数随数据规模增长过快时,例如双重枚举 n 乘 n 次,n 达到十万级就会超时,需要缩小范围或换算法。
怎么减少枚举次数?
先分析哪些值不可能成为答案,缩小上下界;再利用变量之间的等式关系,把一层循环改成直接计算。
枚举和搜索有什么区别?
枚举通常指按固定范围逐个检查;搜索是在解空间中按规则扩展,常配合回溯或剪枝,适合组合类问题。
