学习笔记之组合排列(学习中)
一:基础工具与方法
加法原理:完成一件事情若有 a 种方法与 b 种方法,则总方法数为 a + b 种。
乘法原理:完成一件事情若需前后两步,第一步有 a 种方法,第二步有 b 种方法,则完成这件事有 a * b 种方法。
容斥原理:设 n 个集合 S 为有限集合, |S| 表示集合大小,则:
排列数(符号:A):从 n 个不同元素中取 m 排成一列,可产生排列数量为:
组合数(符号:C):从 n 个元素中取 m 个组成一个集合(无关顺序),可产生集合数量为:
——有关组合数相关性质:
\(C_n^m = C_n^{n - m}\)
\(C_n^m = C_{n - 1}^{m} + C_{n - 1}^{m - 1}\)
\(C_n^0 + C_n^1 + C_n^2 + ... + C_n^n = 2^n\)
——证明如下:
一:对于每个 n 个数中取出 m 个数的集合,剩下 m - n 个数同样构成一个集合,两个集合一一对应,所以性质一成立。
二:从 n 个元素中取 m 个元素构成集合有两类方法:若取第 n 号元素,则再从剩下 n - 1 个元素选 m - 1 个,有 \(C_{n - 1}^{m - 1}\) 种方法。若不取,则从剩下 n - 1 个元素中选 m 个,有 \(C_{n - 1}^{m}\) 种方法。根据加法原理,得 \(C_n^m = C_{n - 1}^{m} + C_{n - 1}^{m - 1}\) ,所以性质二成立。
三:假设从 n 个元素中选出若干个元素构成一个集合,根据加法原理,共有 \(C_n^0 + C_n^1 + C_n^2 + ... + C_n^n = 2^n\) 种方法;又想到每个元素有取与不取两种状态,根据乘法原理可知,共有 \(2^n\) 种方法。即得 \(C_n^0 + C_n^1 + C_n^2 + ... + C_n^n = 2^n\) ,所以性质三成立。
——证毕。
卡特兰数:给定 n 个 -1 与 n 个 -1 ,以某种顺序构成长度为 2n 的序列,满足任意一段前缀和都大于等于 0 的序列个数为:
——证明如下:
易求所有长度为 2n 的序列数为 \(C_{2n}^{n}\) 个。
那么不妨假设满足要求的序列有 A 个,不满足的有 B 组,得 \(A + B = C_{2n}^{n}\) ,那么目标变成了求出 B 是多少。
注意到对于每一个 B 类序列,都存在一个最小位置 2p + 1 , 其中 -1 有 p + 1 个,1 有 p 个。再将区间 [2p + 2 ~ n] 中的数全部取反,得到 -1 有 n - p - 1 个,得到 1 有 n - p 个,此时这个序列有了 n + 1 个 1 与 n - 1 个 -1的序列。于是,我们只要求出这样的序列有几个就可以了,显然有 \(C_{2n}^{n - 1}\) 个。那么便推出式子:
——证毕。
第一类斯特林数:给定 p 个不同对象,将其排成 k 个非空循环排列的方法数为第一类斯特林数:
——证明如下:
给定 p 个对象,将其排成 k 个非空循环排列有两类方法:若将第 p 号元素单独放一个排列,则有 \(s(p - 1, k - 1)\) 种方法;若放入已有排列中,那么它可以在 1 ~ (p - 1) 号元素的左侧,有 \(s(p - 1, k)(p - 1)\) 种方法。则
——证毕。
第二类斯特林数:给定 p 个不同对象,将其组合成 k 个非空集合的方法数为第二类斯特林数:
——证明如下:
给定 p 个对象,将其组合成 k 个非空集合有两类方法:若将第 p 号元素单独放一个集合,则有 \(s(p - 1, k - 1)\) 种方法;若放入已有集合中,那么它可以在 1 ~ k 号集合中,有 $s(p - 1, k)k $ 种方法。则
——证毕。
挡板法:将 n 个相同元素组成 m 个不同非空集合的方法数为:
思想:由于元素相同,排成一排后构成了 n - 1 个空隙,问题便转化成了往 n - 1 个空隙中放置 m - 1 个挡板,得出式子。
挡板法其二:将 n 个相同元素组成 m 个不同集合(集合允许为空)的方法数为:
思想:n - 1 个空隙再加上 m 个可能的空集即可。
DP法:视题目而论,这里不做过多介绍。
关于排列数,组合数,第一第二类斯特林数代码如下:
点击查看代码
int C(int x, int y) {
if (x < y) return 0;
int a = 1;
for (int i = 1; i <= y; i++)
a = a * (x - y + i) / i;
return a;
}
int A(int x, int y) {
if (x < y) return 0;
int a = 1;
for (int i = x - y + 1; i <= x; i++)
a *= i;
return a;
}
int stirling_1(int p, int k) {
if (p == k) return 1;
if (k == 0 || p < k) return 0;
return stirling_1(p - 1, k - 1) + (p - 1) * stirling_1(p - 1, k);
}
int stirling_2(int p, int k) {
if (p == k) return 1;
if (k == 0 || p < k) return 0;
return stirling_2(p - 1, k - 1) + k * stirling_2(p - 1, k);
}
二:经典例题分析
一:1, 2, 3,..., n 经过一个栈,有多少种合法的出栈顺序。
方法:将入栈操作看作 1 ,出栈操作看作 -1 。问题变成了给定 n 个 -1 与 n 个 -1 ,以某种顺序构成长度为 2n 的序列,求满足任意一段前缀和都大于等于 0 的序列个数,用卡特兰数即可。
二:题目:https://www.luogu.com.cn/problem/P10937
方法:将 L 形分成两部分,枚举其中一块里车的数量,易得公式:
易错点:考虑到第一部分要预留 k - i 行给第二部分放,便有了 b + d - i + k 这一式子。

浙公网安备 33010602011771号