排列组合是组合数学中的基础。排列就是指从给定个数的元素中取出指定个数的元素进行排序;组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。排列组合的中心问题是研究给定要求的排列和组合可能出现的情况总数。排列组合与古典概率论关系密切。
在高中初等数学中,排列组合多是利用列表、枚举等方法解题。
完成一个工程可以有 n n n 类办法,a i ( 1 ≤ i ≤ n ) a_i(1 \le i \le n) a i ( 1 ≤ i ≤ n ) 代表第 i i i 类方法的数目。那么完成这件事共有 S = a 1 + a 2 + ⋯ + a n S=a_1+a_2+\cdots +a_n S = a 1 + a 2 + ⋯ + a n 种不同的方法。
完成一个工程需要分 n n n 个步骤,a i ( 1 ≤ i ≤ n ) a_i(1 \le i \le n) a i ( 1 ≤ i ≤ n ) 代表第 i i i 个步骤的不同方法数目。那么完成这件事共有 S = a 1 × a 2 × ⋯ × a n S = a_1 \times a_2 \times \cdots \times a_n S = a 1 × a 2 × ⋯ × a n 种不同的方法。
假设你在一家餐厅点饮料,菜单提供以下互斥 (即不可同时选择)的选择:
咖啡类 :美式、拿铁、卡布奇诺(3 种 )
茶类 :红茶、绿茶(2 种 )
果汁类 :橙汁、苹果汁(2 种 )
问 :你有多少种不同的饮料选择?
因为咖啡、茶、果汁属于不同类别(选择一种就不能选其他类别的饮料),所以总选择数是各类别选项的直接相加 :
3 (咖啡) + 2 (茶) + 2 (果汁) = 7 种
关键点 :加法原理适用于互斥选项 (选咖啡就不能同时选茶或果汁)。
如果是“组合套餐”(如主食 + 饮料),则需要用乘法原理计算。例如:
主食有 4 种,饮料有 7 种 → 总搭配方式 =4 × 7 = 28 4 \times 7 = 28 4 × 7 = 28 种。
这样就能清晰区分两类计数原理的适用场景!
从 n n n 个不同元素中,任取 m m m (m ≤ n m\leq n m ≤ n ,m m m 与 n n n 均为自然数,下同)个元素按照一定的顺序排成一列,叫做从 n n n 个不同元素中取出 m m m 个元素的一个排列;从 n n n 个不同元素中取出 m m m (m ≤ n m\leq n m ≤ n ) 个元素的所有排列的个数,叫做从 n n n 个不同元素中取出 m m m 个元素的排列数,用符号 A n m \mathrm A_n^m A n m (或者是 P n m \mathrm P_n^m P n m )表示。
排列的计算公式如下:
A n m = n ( n − 1 ) ( n − 2 ) ⋯ ( n − m + 1 ) = n ! ( n − m ) ! \mathrm A_n^m = n(n-1)(n-2) \cdots (n-m+1) = \frac{n!}{(n - m)!} A n m = n ( n − 1 ) ( n − 2 ) ⋯ ( n − m + 1 ) = ( n − m )! n !
n ! n! n ! 代表 n n n 的阶乘,即 6 ! = 1 × 2 × 3 × 4 × 5 × 6 6! = 1 \times 2 \times 3 \times 4 \times 5 \times 6 6 ! = 1 × 2 × 3 × 4 × 5 × 6 。
公式可以这样理解:n n n 个人选 m m m 个来排队 (m ≤ n m \le n m ≤ n )。第一个位置可以选 n n n 个,第二位置可以选 n − 1 n-1 n − 1 个,以此类推,第 m m m 个(最后一个)可以选 n − m + 1 n-m+1 n − m + 1 个,得:
A n m = n ( n − 1 ) ( n − 2 ) ⋯ ( n − m + 1 ) = n ! ( n − m ) ! \mathrm A_n^m = n(n-1)(n-2) \cdots (n-m+1) = \frac{n!}{(n - m)!} A n m = n ( n − 1 ) ( n − 2 ) ⋯ ( n − m + 1 ) = ( n − m )! n !
全排列:n n n 个人全部来排队,队长为 n n n 。第一个位置可以选 n n n 个,第二位置可以选 n − 1 n-1 n − 1 个,以此类推得:
A n n = n ( n − 1 ) ( n − 2 ) ⋯ 3 × 2 × 1 = n ! \mathrm A_n^n = n(n-1)(n-2) \cdots 3 \times 2 \times 1 = n! A n n = n ( n − 1 ) ( n − 2 ) ⋯ 3 × 2 × 1 = n !
全排列是排列数的一个特殊情况。
从 n n n 个不同元素中,任取 m ≤ n m \leq n m ≤ n 个元素组成一个集合,叫做从 n n n 个不同元素中取出 m m m 个元素的一个组合;从 n n n 个不同元素中取出 m ≤ n m \leq n m ≤ n 个元素的所有组合的个数,叫做从 n n n 个不同元素中取出 m m m 个元素的组合数,用符号 ( n m ) \dbinom{n}{m} ( m n ) 来表示,读作「n n n 选 m m m 」。
组合数计算公式
( n m ) = A n m m ! = n ! m ! ( n − m ) ! \dbinom{n}{m} = \frac{\mathrm A_n^m}{m!} = \frac{n!}{m!(n - m)!} ( m n ) = m ! A n m = m ! ( n − m )! n !
如何理解上述公式?我们考虑 n n n 个人选 m m m 个出来(m ≤ n m \le n m ≤ n ),不排队,不在乎顺序。如果在乎顺序那么就是 A n m \mathrm A_n^m A n m ,如果不在乎那么就要除掉重复,那么重复了多少?同样选出来的 m m m 个人,他们还要「全排」得 m ! m! m ! ,所以得:
( n m ) × m ! = A n m ( n m ) = A n m m ! = n ! m ! ( n − m ) ! \begin{aligned}
\dbinom{n}{m} \times m! &= \mathrm A_n^m\\
\dbinom{n}{m} &= \frac{\mathrm A_n^m}{m!} = \frac{n!}{m!(n-m)!}
\end{aligned} ( m n ) × m ! ( m n ) = A n m = m ! A n m = m ! ( n − m )! n !
组合数也常用 C n m \mathrm C_n^m C n m 表示,即 C n m = ( n m ) \displaystyle \mathrm C_n^m=\binom{n}{m} C n m = ( m n ) 。现在数学界普遍采用 ( n m ) \dbinom{n}{m} ( m n ) 的记号而非 C n m \mathrm C_n^m C n m 。
组合数也被称为「二项式系数」,下文二项式定理将会阐述其中的联系。
特别地,规定当 m > n m>n m > n 时,A n m = ( n m ) = 0 \mathrm A_n^m=\dbinom{n}{m}=0 A n m = ( m n ) = 0 。
抽屉原理,亦称鸽巢原理(the pigeonhole principle)。
它常被用于证明存在性证明和求最坏情况下的解。
将 n + 1 n+1 n + 1 个物体,划分为 n n n 组,那么有至少一组有两个(或以上)的物体。
这个定理看起来比较显然,证明方法考虑反证法:假如每个分组有至多 1 1 1 个物体,那么最多有 1 × n 1\times n 1 × n 个物体,而实际上有 n + 1 n+1 n + 1 个物体,矛盾。
Catalan 数列 H n H_n H n 可以应用于以下问题:
有 2 n 2n 2 n 个人排成一行进入剧场。入场费 5 元。其中只有 n n n 个人有一张 5 元钞票,另外 n n n 人只有 10 元钞票,剧院无其它钞票,问有多少种方法使得只要有 10 元的人买票,售票处就有 5 元的钞票找零?
有一个大小为 n × n n\times n n × n 的方格图左下角为 ( 0 , 0 ) (0, 0) ( 0 , 0 ) 右上角为 ( n , n ) (n, n) ( n , n ) ,从左下角开始每次都只能向右或者向上走一单位,不走到对角线 y = x y=x y = x 上方(但可以触碰)的情况下到达右上角有多少可能的路径?
在圆上选择 2 n 2n 2 n 个点,将这些点成对连接起来使得所得到的 n n n 条线段不相交的方法数?
对角线不相交的情况下,将一个凸多边形区域分成三角形区域的方法数?
一个栈(无穷大)的进栈序列为 1 , 2 , 3 , ⋯ , n 1,2,3, \cdots ,n 1 , 2 , 3 , ⋯ , n 有多少个不同的出栈序列?
n n n 个结点可构造多少个不同的二叉树?
由 n n n 个 + 1 +1 + 1 和 n n n 个 − 1 -1 − 1 组成的 2 n 2n 2 n 个数 a 1 , a 2 , ⋯ , a 2 n a_1,a_2, \cdots ,a_{2n} a 1 , a 2 , ⋯ , a 2 n ,其部分和满足 a 1 + a 2 + ⋯ + a k ≥ 0 ( k = 1 , 2 , 3 , ⋯ , 2 n ) a_1+a_2+ \cdots +a_k \geq 0~(k=1,2,3, \cdots ,2n) a 1 + a 2 + ⋯ + a k ≥ 0 ( k = 1 , 2 , 3 , ⋯ , 2 n ) ,有多少个满足条件的数列?
其对应的序列为:
H 0 H_0 H 0 H 1 H_1 H 1 H 2 H_2 H 2 H 3 H_3 H 3 H 4 H_4 H 4 H 5 H_5 H 5 H 6 H_6 H 6 ... 1 1 2 5 14 42 132 ...
该递推关系的解为:
H n = ( 2 n n ) n + 1 ( n ≥ 2 , n ∈ N + ) H_n = \frac{\binom{2n}{n}}{n+1}(n \geq 2, n \in \mathbf{N_{+}}) H n = n + 1 ( n 2 n ) ( n ≥ 2 , n ∈ N + )
关于 Catalan 数的常见公式:
H n = { ∑ i = 1 n H i − 1 H n − i n ≥ 2 , n ∈ N + 1 n = 0 , 1 H_n = \begin{cases}
\sum_{i=1}^{n} H_{i-1} H_{n-i} & n \geq 2, n \in \mathbf{N_{+}}\\
1 & n = 0, 1
\end{cases} H n = { ∑ i = 1 n H i − 1 H n − i 1 n ≥ 2 , n ∈ N + n = 0 , 1
H n = H n − 1 ( 4 n − 2 ) n + 1 H_n = \frac{H_{n-1} (4n-2)}{n+1} H n = n + 1 H n − 1 ( 4 n − 2 )
H n = ( 2 n n ) − ( 2 n n − 1 ) H_n = \binom{2n}{n} - \binom{2n}{n-1} H n = ( n 2 n ) − ( n − 1 2 n )
例题
P1044 栈
题目大意:入栈顺序为 1 , 2 , … , n 1,2,\ldots ,n 1 , 2 , … , n ,求所有可能的出栈顺序的总数。
示例代码
#include <iostream>
using namespace std ;
int n;
long long f[ 25 ];
int main () {
f[ 0 ] = 1 ;
cin >> n;
for ( int i = 1 ; i <= n; i ++ ) f[i] = f[i - 1 ] * ( 4 * i - 2 ) / (i + 1 );
// 这里用的是常见公式 2
cout << f[n] << endl;
return 0 ;
}