Skip to content

第2章 计数原理 ​


🎯 本章学习目标 ​

通过本章的学习,你将能够:

  1. 掌握加法原理:理解分类计数思想,能判断何时用加法
  2. 掌握乘法原理:理解分步计数思想,能判断何时用乘法
  3. 辨析两大原理:能准确区分"分类"还是"分步",选择正确的运算
  4. 组合使用原理:能解决同时需要分类和分步的复杂计数问题
  5. 避免常见错误:知道分类不重不漏、分步相互独立等注意事项
  6. 解决典型问题:能解决路线、染色、数位、网格路径等经典计数问题
  7. 理解容斥思想:知道分类重叠时需要用容斥原理修正
  8. 建立计数直觉:培养"不重不漏"的计数思维
  9. 理解编程意义:知道计数原理是DP、组合数学的基础
  10. 应对真题题型:能解决选择、判断、计算等各类计数原理题目

🗺️ 本章知识图谱 ​

计数原理
├── 加法原理(分类计数)
│   ├── 核心:完成一件事有n类办法
│   ├── 公式:总数 = m1 + m2 + ... + mn
│   ├── 关键词:要么...要么...
│   ├── 要求:类与类之间互斥(不重不漏)
│   └── 典型场景
│       ├── 多种交通方式选一种
│       ├── 多类套餐选一种
│       └── 分类讨论问题
├── 乘法原理(分步计数)
│   ├── 核心:完成一件事分n个步骤
│   ├── 公式:总数 = m1 × m2 × ... × mn
│   ├── 关键词:先...再...
│   ├── 要求:步与步之间独立
│   └── 典型场景
│       ├── 路线中转(甲→乙→丙)
│       ├── 多步选择(衣裤搭配)
│       └── 排列问题(逐步选位置)
├── 辨析方法
│   ├── 看关系:互斥用加,独立用乘
│   ├── 看描述:要么...要么...=加;先...再...=乘
│   └── 验证:用小例子手动枚举
├── 组合使用
│   ├── 先分类,每类内部分步
│   └── 先分步,每步内部分类
├── 易错点
│   ├── 分类有重叠 → 容斥原理
│   ├── 分步不独立 → 重新分步
│   ├── 首位不能为0 → 单独处理
│   └── 染色问题 → 先染枢纽区域
└── 典型应用
    ├── 路线计数
    ├── 染色问题
    ├── 数位计数
    ├── 网格路径
    └── 排队问题

一、🧭 为什么八级还要学"数数"? ​

很多同学觉得"加法、乘法"是小学知识,八级考这个是不是太简单?恰恰相反。 GESP 八级的计数题,难的不再是计算,而是建模:

  • 一道网格路径题,到底是 3+2=5 还是 3×2=6?判错符号,答案全错;
  • 一道染色题,分步顺序选错,就会重复计数或遗漏方案;
  • 一道数位编程题,分类标准不互斥,代码写得再漂亮也是错的。

🔥 高频考点:加法原理与乘法原理的辨析,几乎是八级选择题/判断题的"必点菜";而编程题中的路径计数、数位统计、方案数 DP,其状态转移方程本质上就是两大原理的反复套用。


二、加法原理:分类计数 ➕ ​

定义:完成一件事有 n 类办法,第 1 类有 m1m_1 种方法,第 2 类有 m2m_2 种方法……第 n 类有 mnm_n 种方法,且各类办法相互独立、互斥(任选一类中的一种即可完成这件事),那么完成这件事共有:

N=m1+m2+⋯+mnN = m_1 + m_2 + \cdots + m_n

生活实例:食堂午餐有 3 种中餐套餐、4 种面食、2 种西餐,只选一份,共有 3+4+2=93+4+2=9 种选择。

关键词:"或"——选中餐或面食或西餐,选哪一类都能独立完成任务。

⚠️ 易错点:分类必须"不重不漏" 各类办法之间不能有重叠(同一方案被算进两类 → 重复计数),也不能有遗漏(某类方案没被统计 → 结果偏小)。这就是数学上常说的分类要满足 MECE(相互独立、完全穷尽)。


三、乘法原理:分步计数 ✖️ ​

定义:完成一件事需要 n 个步骤,做第 1 步有 m1m_1 种方法,做第 2 步有 m2m_2 种方法……做第 n 步有 mnm_n 种方法,各步骤相继完成、缺一不可,那么完成这件事共有:

N=m1×m2×⋯×mnN = m_1 \times m_2 \times \cdots \times m_n

生活实例:一份套餐含 1 荤(3 种选择)、1 素(4 种选择)、1 汤(2 种选择),各选一样配成一餐,共有 3×4×2=243\times4\times2=24 种搭配。

关键词:"且"——选荤且选素且选汤,每一步都走完才算完成。

🔥 高频考点:乘法原理中,后一步的方法数可能依赖前一步的选择(如染色问题中"相邻区域不同色",第二步比第一步少 1 种选择),但只要"每一步的选择数"能确定,就照常相乘。


四、判断标准:分类用加,分步用乘 🤔 ​

拿到一道计数题,先问自己三个问题:

判断问题回答"是"回答"否"
这件事是"选一种就完事"吗?分类 → 加法继续往下问
这件事需要"一步步全做完"吗?分步 → 乘法需要拆分重组
各类/各步之间会互相影响吗?受影响的步骤单独算选择数直接套用

一句话口诀:

📌 分类用加("或"的关系),分步用乘("且"的关系);分类要不重不漏,分步要顺序合理。

对比辨析:

对比维度加法原理乘法原理
逻辑关系分类(或)分步(且)
完成条件任取一类中的一种即完成所有步骤全部完成才完成
各类/步关系相互独立、互斥相继进行,缺一不可
典型错误分类重叠 / 遗漏分步顺序不当导致重复计数

五、两大原理的组合使用 🧩 ​

复杂题目往往需要两大原理嵌套使用,有两种典型结构:

① 先分类,后分步:先把方案分成几大类(加法),每一大类内部再分步计算(乘法)。

例:选餐问题中"吃套餐"分中餐、面食两类(加),每类都要选主食+饮料(乘)。

② 先分步,后分类:先把事情拆成几个步骤(乘法骨架),某一步内部存在多种情况需要分类讨论(加法填充)。

例:染色问题分步给区域染色(乘),染某个区域时要按"与前一区域同色/不同色"分类讨论(加)。

💡 经验:真题中"先分类后分步"更常见。拿到题先按最显眼的差异分类(如位数、奇偶、是否经过某点),再在每类内分步。


六、典型例题(难度递进) ​

例1(单选风格)|选餐问题 ​

题目:某餐厅菜单上有中餐套餐 5 种、面食 3 种、小吃 4 种。小明打算:① 只点一种主食,有多少种点法?② 点一种主食 + 一种小吃,有多少种点法?

解析: 注意"主食"和"小吃"是两个独立类别——主食 = 中餐套餐 + 面食,共 5+3=85+3=8 种;小吃 4 种单独算。

  • ① 从主食类中任选一种,分类计数:5+3=85+3=8 种(加法原理)。
  • ② 选主食和选小吃是两个都要完成的步骤:8×4=328\times4=32 种(乘法原理)。

⚠️ 易错点:别把小吃也归为主食。如果误写成 5+3+4=125+3+4=12 再乘 12×4=4812\times4=48,就会犯两类错误:① 多算 4 种;② 把小吃的 4 种重复乘了一次。正确的分步交叉验证:中餐配小吃 5×4=205\times4=20 + 面食配小吃 3×4=123\times4=12 = 32,结果一致。

代码验证(枚举所有点法):

cpp
#include <iostream>
using namespace std;
int main() {
    // 主食 = 中餐套餐 + 面食,小吃独立
    int staple_cn  = 5;   // 中餐套餐
    int staple_face = 3;  // 面食
    int snack      = 4;   // 小吃
    int staple     = staple_cn + staple_face;  // 主食 = 8
    int combo      = staple * snack;            // 主食+小吃 = 32
    cout << "只点主食: " << staple << endl;  // 8
    cout << "主食+小吃: " << combo << endl;  // 32
    return 0;
}

例2(单选风格)|网格路径计数 ​

题目:如图,从街道的 A 点走到 B 点,只能向右或向下走,共需向右走 3 段、向下走 3 段。问共有多少条不同路线?

解析:对每个路口,到达它的路线只有两个来源:从正上方下来、从正左边过来。两类路线互斥,用加法原理递推:

dp[i][j]=dp[i−1][j]+dp[i][j−1]dp[i][j] = dp[i-1][j] + dp[i][j-1]

边界:第一行、第一列的路口都只有 1 条路线。逐格算出终点值为 20。

🔥 高频考点:这个递推就是著名的"网格路径数",答案本质是组合数 C63=20C_6^3=20——为什么?因为 6 步里选 3 步向右(其余向下),这正是第3期排列组合要讲的内容,先混个脸熟!

代码验证:

cpp
#include <iostream>
using namespace std;
int main() {
    int n = 3, m = 3;            // 向右 n 段,向下 m 段
    long long dp[20][20] = {0};
    dp[0][0] = 1;
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= m; j++) {
            if (i == 0 && j == 0) continue;
            if (i > 0) dp[i][j] += dp[i-1][j];  // 从上方来(一类)
            if (j > 0) dp[i][j] += dp[i][j-1];  // 从左方来(另一类)
        }
    cout << dp[n][m] << endl;    // 20
    return 0;
}

💡 注意:dp[i][j] = 上方 + 左方 这一步,就是把加法原理写进了代码——DP 计数题的转移方程,几乎都能找到计数原理的影子。


例3(单选风格)|数位问题:1~999 中不含数字 5 的数 ​

题目:在 1 到 999 的整数中,十进制表示里不含数字 5 的数共有多少个?

解析:这是典型的先分类、后分步:

  • 第一类:一位数。1~9 中去掉 5,共 8 个;
  • 第二类:两位数。十位可取 1~9 除 5(8 种),个位可取 0~9 除 5(9 种),分步相乘:8×9=728\times9=72 个;
  • 第三类:三位数。百位 8 种,十位 9 种,个位 9 种:8×9×9=6488\times9\times9=648 个。

三类相加:8+72+648=7288+72+648=\mathbf{728} 个。

⚠️ 易错点:两位数/三位数的首位不能为 0,所以首位是"8 种"而不是"9 种";其余位才是 9 种。数位问题中"首位特判"是第一大坑。

代码验证(暴力枚举,小数据范围可直接检验):

cpp
#include <iostream>
using namespace std;
int main() {
    int cnt = 0;
    for (int x = 1; x <= 999; x++) {
        int t = x, ok = 1;
        while (t) {
            if (t % 10 == 5) { ok = 0; break; }
            t /= 10;
        }
        cnt += ok;
    }
    cout << cnt << endl;   // 728
    return 0;
}

例4(单选风格)|染色问题入门 ​

题目:地图上有 A、B、C 三个区域,其中 A 与 B 相邻、B 与 C 相邻,A 与 C 不相邻。用 4 种颜色给三个区域染色,要求相邻区域颜色不同,共多少种方案?

**解析:**分步染色,优先染"相邻关系最多"的区域 B:

  • 第 1 步染 B:4 种选择;
  • 第 2 步染 A:与 B 不同色,3 种;
  • 第 3 步染 C:只需与 B 不同色(A、C 不相邻,可以同色),3 种。

乘法原理:4×3×3=364\times3\times3=\mathbf{36} 种。

变式:若 A 与 C 也相邻(三个区域两两相邻),则 C 还要与 A 不同色:4×3×2=244\times3\times2=24 种。

⚠️ 易错点: ① 染色顺序很关键!若先染 A 再染 B 再染 C,本题也是 4×3×3=364\times3\times3=36,但遇到环形、复杂相邻图时,顺序选错会让后染的区域约束说不清,被迫分类讨论甚至重复计数。口诀:先染邻居多的"枢纽"区域。 ② "不相邻"意味着"可同色可不同色",约束只有"相邻不同色"一条,别擅自加戏。

代码验证(枚举所有染色方案):

cpp
#include <iostream>
using namespace std;
int main() {
    int cnt = 0;
    for (int a = 0; a < 4; a++)
        for (int b = 0; b < 4; b++)
            for (int c = 0; c < 4; c++)
                if (a != b && b != c)   // 仅 A-B、B-C 相邻
                    cnt++;
    cout << cnt << endl;   // 36
    return 0;
}

例5(编程风格计数题)|统计不含数字 4 的数 ​

题目:给定正整数 nn(1≤n≤1091\le n\le 10^9),统计 1∼n1\sim n 中十进制表示不含数字 4 的整数个数。

思路(先分类后分步):

  • 第一类:位数比 n 少的数。k 位数中,首位可取 1~9 除 4(8 种),其余每位可取 0~9 除 4(9 种),故 k 位数有 8×9k−18\times9^{k-1} 个——类内分步用乘法,各位数之间用加法求和。
  • 第二类:与 n 位数相同的数。从高位到低位逐位扫描:当前位取"比 n 该位小且不等于 4"的每种数字时(分类,加法累计),后面剩余位可任意取(每位 9 种,乘法);若 n 自身某位就是 4,则含 4 的前缀之后不必再统计,直接停止;扫描结束若 n 本身不含 4,再补上 n 自己。
🔑 点击查看答案与解析

参考代码(C++11):

cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
    long long n;
    cin >> n;
    string s = to_string(n);
    int len = s.size();

    long long p9[15];
    p9[0] = 1;
    for (int i = 1; i < 15; i++) p9[i] = p9[i-1] * 9;

    long long ans = 0;
    // 第一类:位数少于 len 的数(按位数分类,加法原理)
    long long ways = 8;                 // 1 位数:首位 8 种
    for (int k = 1; k < len; k++) {
        ans += ways;                    // k 位数的合法个数
        ways *= 9;                      // 位数每多一位,后位 9 种(乘法)
    }

    // 第二类:与 n 同位数,逐位分类讨论
    bool valid = true;
    for (int i = 0; i < len; i++) {
        int d = s[i] - '0';
        int lo = (i == 0 ? 1 : 0);      // 首位不能为 0
        int choices = 0;
        for (int x = lo; x < d; x++)    // 当前位可取的更小数字
            if (x != 4) choices++;      // 分类:每种选择互斥
        int remain = len - i - 1;
        ans += choices * p9[remain];    // 后 remain 位任意:9^remain(乘法)
        if (d == 4) { valid = false; break; }  // n 前缀含 4,后续无需统计
    }
    if (valid) ans += 1;                // n 本身不含 4

    cout << ans << endl;
    return 0;
}

自测样例:输入 10 → 输出 9(1,2,3,5,6,7,8,9,10);输入 4 → 输出 3(1,2,3);输入 999 → 输出 728(与例3互相印证 ✓)。

🔥 高频考点:这类"数位计数"题是八级编程题的常客。暴力枚举在 n≤109n\le10^9 时会超时,而用计数原理按位拆分后是 O(log⁡n)O(\log n)——这就是"用数学代替枚举"的威力,也是第9期算法优化的伏笔。


七、易错点清单 📋 ​

易错点错误表现正确做法
分类重叠同一方案被算进多个类别,结果偏大分类标准互斥(MECE),算完反问"有没有方案被数了两次"
分类遗漏某类方案没考虑到,结果偏小分类后检验边界情况(首位为0、空方案、n自身)
分步顺序错误后一步的选择数说不清,或隐性重复优先安排约束最多的步骤(如染色先染枢纽区域)
分步误用加法"甲到乙3条路、乙到丙2条路"写成 3+2路线需两段都走,分步用乘:3×2=6
分类误用乘法"三类套餐只选一种"写成 5×3×4任选一类即完成,分类用加
首位特判遗漏数位题中首位按 9 种/10 种算首位不能为 0,单独确定选择数

八、与下一章的衔接:从"原理"到"公式" 🔗 ​

本章两大原理回答的是"怎么数",但当对象变多时——

  • 从 5 个人中选 3 个排成一排,有多少种排法?(乘法原理连乘:5×4×35\times4\times3)
  • 从 5 个人中选 3 个组成小组(不排序),有多少种选法?(需要"分组"新工具)
  • 例2 的网格路径数为什么恰好等于 C63C_6^3?

这些问题的系统化答案,就是排列数 AnmA_n^m 与组合数 CnmC_n^m。它们不是新东西,而是加法/乘法原理在"有序选取"与"无序选取"两种场景下的浓缩公式;再往后,第4章的杨辉三角本质就是组合数的递推表 Cnm=Cn−1m−1+Cn−1mC_n^m=C_{n-1}^{m-1}+C_{n-1}^{m}——看,又是加法原理!

💡 计数原理是整条数学链的"地基":本章学牢,第3章排列组合、第4章杨辉三角都会事半功倍。


九、📝 实战练习 ​

练习题 1(单选题) ​

题目: 小明有 3 本不同的数学书、4 本不同的语文书、5 本不同的英语书。他要选一本书带出门,共有多少种选法?( )

A. 12
B. 60
C. 3+4+5=12
D. 3×4×5=60

🔑 点击查看答案与解析

答案: A

解析: "选一本书带出门"——要么选数学书,要么选语文书,要么选英语书。这是分类,用加法原理。

总选法 = 3 + 4 + 5 = 12

选 A。

💡 关键词"选一本"= 只选一个类别里的一本 → 分类用加法。

---

练习题 2(单选题) ​

题目: 小明有 3 件不同的上衣、4 条不同的裤子、2 双不同的鞋子。一套穿搭包括一件上衣、一条裤子和一双鞋子,共有多少种不同的穿搭方式?( )

A. 9
B. 24
C. 3+4+2=9
D. 3×4×2=24

🔑 点击查看答案与解析

答案: B

解析: 搭配一套衣服,需要"先选上衣,再选裤子,再选鞋子"——这是分步,用乘法原理。

总穿搭数 = 3 × 4 × 2 = 24

选 B。

🍎 生活比喻:每选一件上衣,都可以搭配 4 条裤子;每一种衣裤搭配,又可以配 2 双鞋。所以是乘的关系。

---

练习题 3(判断题) ​

题目: "从 5 个人中选 1 个人当班长,再选 1 个人当副班长(不能同一人)",这道题用加法原理,选法有 5 + 4 = 9 种。( )

🔑 点击查看答案与解析

答案: 错误 ❌

解析: "先选班长,再选副班长"——有先后顺序,是分步,用乘法原理。

选法 = 5 × 4 = 20 种,不是 9 种。

什么时候用加法?比如"选一个人当学生代表,可以从5个男生或4个女生中选"——这是分类,用加法(5+4=9)。

🔥 高频考点:判断用加还是用乘,就看是"选一个"还是"都要选"。选一个 = 加法;都要选 = 乘法。

---

练习题 4(单选题) ​

题目: 从 A 到 B 有 3 条路,从 B 到 C 有 2 条路,从 A 到 C 有 4 条直达路线。从 A 到 C 共有多少种不同的走法?( )

A. 9
B. 10
C. 24
D. 5

🔑 点击查看答案与解析

答案: B

解析: 从 A 到 C 有两类走法:

  1. 经 B 中转:A→B→C,分步用乘法 → 3 × 2 = 6 种
  2. 直达:4 种

两类走法互斥(要么直达,要么中转),所以用加法:

总走法 = 6 + 4 = 10 种

选 B。

💡 解题步骤:先分类(直达 vs 中转),每类内部再看是分步还是分类。

---

练习题 5(单选题) ​

题目: 用数字 1, 2, 3, 4, 5 可以组成多少个不同的两位数(数字可以重复使用)?( )

A. 10
B. 20
C. 25
D. 15

🔑 点击查看答案与解析

答案: C

解析: 组成两位数,分两步:

  1. 选十位:有 5 种选择(1~5)
  2. 选个位:有 5 种选择(可以重复,所以还是 5 种)

分步用乘法:5 × 5 = 25

选 C。

⚠️ 如果题目说"数字不能重复",就是 5 × 4 = 20。注意看清楚题目要求!

---

练习题 6(单选题) ​

题目: 用 3 种颜色给下图中 3 个区域染色,要求相邻区域颜色不同。共有多少种染色方案?(区域 A 与 B 相邻,B 与 C 相邻,A 与 C 不相邻)( )

A. 6
B. 8
C. 12
D. 27

🔑 点击查看答案与解析

答案: C

解析: 按顺序染色,先染中间的 B(枢纽区域):

  1. 染 B:3 种选择
  2. 染 A:不能和 B 同色 → 2 种选择
  3. 染 C:不能和 B 同色,但可以和 A 同色 → 2 种选择

分步用乘法:3 × 2 × 2 = 12

选 C。

💡 技巧:染色问题通常先染约束最多的区域(和最多邻居相邻的),也就是"枢纽区域"。

---

练习题 7(单选题) ​

题目: 在一个 3×2 的网格中,从左上角走到右下角,每次只能向右或向下走一步,共有多少种走法?( )

A. 5
B. 6
C. 10
D. 12

🔑 点击查看答案与解析

答案: C

解析: 3×2 的网格,从左上到右下:

  • 需要向下走 3 步
  • 需要向右走 2 步
  • 一共走 5 步

问题转化为:在 5 步中选 3 步向下(剩下 2 步向右),有多少种选法?

这是组合数 C(5,3) = C(5,2) = 5×4 / 2×1 = 10

也可以用 DP 验证:

  1  1  1
  1  2  3
  1  3  6
  1  4 10

最后一个格子是 10 ✓

选 C。

💡 m×n 网格从左上到右下(只能右/下)的路径数 = C(m+n, m) = C(m+n, n)

---

练习题 8(判断题) ​

题目: 一个班有 30 人,其中 15 人喜欢数学,20 人喜欢语文。那么喜欢数学或语文的总人数一定是 15 + 20 = 35 人。( )

🔑 点击查看答案与解析

答案: 错误 ❌

解析: 加法原理的前提是"类与类互斥(不重叠)"。但这里"喜欢数学"和"喜欢语文"可能有重叠(有人两科都喜欢),所以不能直接相加。

正确做法(容斥原理): 喜欢数学或语文的人数 = 喜欢数学的 + 喜欢语文的 - 两科都喜欢的

因为有重叠,所以总数一定小于等于 35,而且不可能超过全班 30 人。

🔥 高频考点:分类时一定要注意"不重不漏"。如果有重叠,要用容斥原理减回去。

---

十、📊 考点总结表 ​

考点重要程度出题形式易错点
加法原理(分类)⭐⭐⭐⭐⭐选择/判断分类必须互斥不重叠
乘法原理(分步)⭐⭐⭐⭐⭐选择/判断分步必须相互独立
两大原理辨析⭐⭐⭐⭐⭐选择/判断选一个=加,都要选=乘
组合使用(先分类再分步)⭐⭐⭐⭐选择/计算分清哪层是分类哪层是分步
路线计数⭐⭐⭐⭐选择/计算直达和中转是两类
染色问题⭐⭐⭐⭐选择/编程先染枢纽区域;相邻不同色
数位计数⭐⭐⭐⭐选择/编程首位不能为0
网格路径计数⭐⭐⭐⭐⭐选择/DPC(m+n,m) 或 DP递推
容斥思想⭐⭐⭐⭐选择/判断重叠部分要减回去
不重不漏原则⭐⭐⭐⭐⭐判断/选择计数的基本要求

⭐ 必背速记卡 ​

┌─────────────────────────────────────────────┐
│       计数原理 · 必背速记卡                 │
├─────────────────────────────────────────────┤
│  1. 加法原理:分类计数,类与类互斥           │
│  2. 乘法原理:分步计数,步与步独立           │
│  3. 选一个 = 加法;都要选 = 乘法             │
│  4. 要么...要么... = 分类 = 加法             │
│  5. 先...再... = 分步 = 乘法                 │
│  6. 组合使用:先分类,每类内部分步           │
│  7. 计数原则:不重不漏                       │
│  8. 分类有重叠 → 容斥原理减回去              │
│  9. 路线中转:分步相乘;直达+中转:分类相加  │
│ 10. 染色问题:先染枢纽区域(邻居最多的)     │
│ 11. 数位计数:首位不能为0,单独处理          │
│ 12. 数字可重复:每步都有n种                  │
│ 13. 数字不重复:每步少一种                   │
│ 14. 网格路径(右+下):C(m+n, m)             │
│ 15. 计数原理是排列组合和DP的基础             │
└─────────────────────────────────────────────┘

百炼成钢,融会贯通