跳转至

枚举入门:一个一个试,找出符合条件的答案

什么时候用?

如果候选答案不多,可以全部试一遍:检查每一个,把符合条件的留下来。这种方法叫“枚举”。

先会:for 循环、条件判断。这里的“枚举”是解题方法,不是 C++ 的 enum 类型。

写循环之前,回答三个问题

  1. 枚举谁?例如整数 x、购买数量 a、位置 i。
  2. 从哪里到哪里?必须保证答案不会漏掉。
  3. 怎样判断符合要求?把题目条件变成 if。

最后再想:要输出所有答案、数答案个数,还是找最好的一个?

例一:找出 1 到 n 中能被 3 整除的数

约定 1 <= n <= 1000。

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    int count = 0;
    for (int x = 1; x <= n; x++) {
        if (x % 3 == 0) {
            cout << x << ' ';
            count++;
        }
    }
    cout << '\n' << count << '\n';
    return 0;
}

输入 10,第一行输出 3 6 9(末尾有空格),第二行输出 3。输入 2,第一行为空行,第二行是 0。

每个候选数恰好检查一次,所以不会重复;范围从 1 到 n,所以不会漏掉。这份代码同时展示“列出”和“计数”,做自己的题时只保留题目要求的输出。

例二:两种笔,正好花完 n 元

一种笔 3 元一支,另一种 5 元一支。每种可以买 0 支,正好花完 n 元,有多少种购买方案?同一种笔的购买顺序不区分。

约定 0 <= n <= 1000。设两种笔各买 a、b 支,条件就是 3 * a + 5 * b == n。

两层枚举:先把思路写清楚

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    int count = 0;
    for (int a = 0; a <= n / 3; a++) {
        for (int b = 0; b <= n / 5; b++) {
            if (3 * a + 5 * b == n) {
                count++;
            }
        }
    }
    cout << count << '\n';
    return 0;
}

输入 15,输出 2,方案是 (a, b) = (0, 3) 和 (5, 0)。输入 1 输出 0。

为什么从 0 开始?因为某种笔可以不买。为什么最多 n / 3 支?再多一支,光第一种笔就超预算了。

少试一些:枚举 a,算出 b

已知买了 a 支第一种笔,剩余 rest = n - 3 * a 元。只要剩余钱能被 5 整除,就恰好对应一种非负整数 b。

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    int count = 0;
    for (int a = 0; a <= n / 3; a++) {
        int rest = n - 3 * a;
        if (rest % 5 == 0) {
            count++;
        }
    }
    cout << count << '\n';
    return 0;
}

本例允许 n 为 0,此时“不买任何笔”是一种方案,答案是 1。如果题目要求至少买一支,就要额外排除它。

两层写法的检查次数最多约为 (n / 3 + 1) × (n / 5 + 1),随 n 的平方增长,写作 O(n²);一层写法是 O(n)。两种方法都正确,但少试一些会更快。

容易出错的地方

  • 漏端点:题目包括 R,就不能写成 x < R。
  • 重复计数:选两个人组队时,(1, 2) 和 (2, 1) 通常是同一队,可以令第二个编号从 i + 1 开始。
  • 遗漏零:允许“不选”“不买”时,要考虑从 0 开始。
  • 找到一个就停止:要统计所有方案时,不能找到第一种就 break。
  • 范围太大:十亿个候选数并不适合逐个检查。先估计循环次数,不存在“所有枚举题都能过”的模板。

自己练习

把例一改为统计 [1, n] 内“能被 3 整除,但不能被 2 整除”的数。n = 10 时有哪些?

提示与答案

条件是 x % 3 == 0 && x % 2 != 0。符合条件的是 3、9,共 2 个。

下一步可以组合方法:枚举区间并数 2、逐个判断回文、用打擂台选最优值。熟练以后再看普及组的暴力枚举。