好上学,职校招生与学历提升信息网。

分站导航

热点关注

好上学在线报名

在线咨询

8:00-22:00

当前位置:

好上学

>

职校资讯

>

学习经验

组合数公式怎么算(组合数公式怎么记忆)

来源:好上学   时间:2022-09-05

在算法学习中,数学主要分为「数论」与「组合计数」两大部分。相比「数论」,「组合计数」通常是作为一种计数思想与其他题型(例如「动态规划」)相结合,进行统一考察。

刷算法题必备的数学考点汇总 中,我们已针对「数论」进行了讲解,本文将介绍「组合计数」。

一、排列组合加法原理

若完成一个任务的方法有 n 类,其中第 i 类方法有

种不同的做法,则完成这个任务共有

种不同的做法。

乘法原理

若完成一个任务需要分 n 个步骤,其中第 i 个步骤有

种不同的做法,且这些做法互不干扰,则完成这个任务共有

种不同的做法。

排列数

从 n 个不同元素中依次取 m 个元素排成一列

,产生的不同排列的数量为:

上述公式可以这样理解:n 个人选 m 个人来排队,第一个位置可以选 n 个,第二个位置可以选 n - 1 个,以此类推,第 m 个位置可以选 n - m 1 个。再根据乘法原理,便可得到上述公式。

另外需要注意,0! = 1。

组合数

从 n 个不同元素中任取 m 个元素组成一个集合

,产生的不同集合的数量为:

上述公式可以这样理解:n 个元素选 m 个组成一个集合,集合数量为

,其中每个集合均有 m! 种不同的排列方案。因此 n 个元素选 m 个组成一个排列,排列方案总数为

,由此可得上述公式。

组合数性质

组合数的性质较多,此处仅列举两个较为重要的性质进行讲解。

首先是「组合数递推公式」,即

。该公式可以这样理解:n 个元素选 m 个组成一个集合,若第 n 个元素取,则方案数为

;若不取,则方案数为

。再根据加法原理,即可得到该递推式。

其次是「二项式定理」,即

,该式可使用「数学归纳法」证明,此处不再赘述。

多重集问题

多重集是指包含重复元素的广义集合。设集合 S 是由

组成的多重集,其中

多重集的排列数

从集合 S 中选取 n 个元素组成排列,其数量为

,即集合 S 的全排列个数。

该式子可以这样理解:对于每一类

,其重复出现的次数为

因此在 n! 的基础上除以所有

即可得到上述答案。

多重集的组合数

从集合 S 中选取 r

个元素组成一个多重集,其方案数为

该式子可以这样理解:由于

因此该问题等价于在每一类

中选取

个元素,使得

的方案数。

转化后的问题可由「插板法」解决,假设当前共有 r k - 1 个位置,现需在其中 k - 1 个位置插上一块分隔板,将原来的 r k - 1 个位置分隔成 k 个区域,其中第 i 个区域的空位置数即为

由此可以得知

的方案数等价于 r k - 1 个位置中选 k - 1 个位置插板的方案数,即答案为

二、常见计数模型不相邻的排列

在 1 到 n 这 n 个自然数中选 k 个,使得 k 个数中任意两个数都不相邻的方案数共

个。该问题等价于当前有蓝色小球 k 个,红色小球 n - k 个,现要将蓝色小球插入红色小球的间隙中,且每个间隙最多插入 1 个蓝色小球,共有多少种方案。

对于每一种插入方案,再插入完后对小球从左到右,从 1 开始编号,其中蓝色小球的编号就是原问题中选取的 k 个自然数。由于每个间隙最多插入 1 个蓝色小球,因此可以保证选取的 k 个数中任意两个都不相邻,即转换后的问题与原问题等价,由此可知总方案数为

个。

错位排列

n 个不同的小球,编号分别为 1 到 n,现在要将这 n 个小球进行排列,使得第 i 个位置上的小球,其编号不为 i,求排列方案数。

令 f[n] 表示 n 个小球的错排方案数,则思考 f[n] 可以由哪些状态一步转移过来?

假设当前编号为 n 的小球放在第 n 个位置,则 n 个球的错排只能从以下两种情况一步转移而来:

1. 前面 n - 1 个小球编号与位置均不同

2. 前面只有一个小球 i 的编号与位置相同

对于第一种情况,可以任选一个小球与第 n 个小球进行位置交换,使其满足错排要求,即

而对于第二种情况,可以令小球 i,n 交换来满足题意。由于 i 共有 n - 1 种可能,因此

综合上述两种情况,

,其中

圆排列

n 个人围成一圈,所有的圆排列数记为

对于每一个圆排列,从不同的位置断开,都可以变成一个新的排列,因此可知

,即

进一步延伸可知,从 n 个人中选 r 个人围成一圈,其圆排列数为

三、康托展开

康托展开可以在 O(n^2) 的时间复杂度内(可用树状数组优化到 O(nlogn)) 求得任意一个 n 个数的排列的排名。

此处将 n 个数的所有排列按字典序排序,任意一个排列的位次即为它的排名。例如排列 [2,4,3,5,1] 的字典序小于排列 [2,4,5,3,1],则排列 [2,4,3,5,1] 的排名更小。

康托展开主要是一种算法思想,我们以排列 [2,5,3,4,1] 为例来进行阐述。

我们通过从左到右依次考虑各个位置的方式来计算答案。首先是第一个位置,不难发现排列 [2,5,3,4,1] 大于所有「以 1 开头的排列」,而以 1 开头的排列共有 4!个。

接下来考虑第二个位置,它大于所有「第一位与当前排列相同,而第二位小于 5 的排列」,由于 2 已经在第一位出现了,因此第二位可以为 1,3,4,即第二位所贡献的答案为 3*3!。

以此类推,最终答案为 1 4! 3*3! 2! 1 = 46,由于统计的是排名,因此最开头需要加上一个 1。

根据上述思想,我们即可通过依次考虑每一位的贡献计算一个排列的排名。

四、习题练习62. 不同路径题目描述

一个机器人位于一个 n x m 网格的左上角,即 (1,1)。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角,即 (n,m)。

问总共有多少条不同的路径?

示例 1

输入: n = 2, m = 3输出: 3解释:从左上角开始,总共有 3 条路径可以到达右下角。1. 向右 -> 向右 -> 向下2. 向右 -> 向下 -> 向右3. 向下 -> 向右 -> 向右示例 2

输入: n = 3, m = 7输出: 28提示


题目数据保证答案小于等于 2* 10^9

解题思路

我们从「组合计数」的角度来考虑这道题,从(n,m)走到 (1,1),每次只能向下或向右移动一步,因此不难发现,一共需要移动 n m - 2 步,其中 n - 1 步向下,m - 1 步向右。

因此问题转化为在 n m - 2 个位置中选 n - 1 个位置填入「向下」,其余位置填入「向右」的方案数,即最终答案为


代码实现

class Solution {public: int C[200][200]; int Count(int m, int n) { if (n == 0 || m == n) return 1; if (C[m][n]) return C[m][n]; return (C[m][n] = Count(m-1, n) Count(m-1, n-1)); } int uniquePaths(int m, int n) { return Count(m n-2, n-1); }};


1359. 有效的快递序列数目题目描述

给你 n 笔订单,每笔订单都需要快递服务。

请你统计所有有效的「收件/配送」序列的数目,确保第 i 个物品的配送服务 delivery(i) 总是在其收件服务 pickup(i) 之后。

由于答案可能很大,请返回答案对 10^9 7 取余的结果。

示例 1

输入:n = 1输出:1解释:只有一种序列 (P1, D1),物品 1 的配送服务(D1)在物品 1 的收件服务(P1)后。示例 2

输入:n = 2输出:6解释:所有可能的序列包括:(P1,P2,D1,D2),(P1,P2,D2,D1),(P1,D1,P2,D2),(P2,P1,D1,D2),(P2,P1,D2,D1) 和 (P2,D2,P1,D1)。(P1,D2,P2,D1) 是一个无效的序列,因为物品 2 的收件服务(P2)不应在物品 2 的配送服务(D2)之后。示例 3

输入:n = 3输出:90提示解题思路

该题有两种做法,分别是「动态规划」与「组合计数」。首先介绍「动态规划」的做法,令 f[i] 表示 i 笔订单的答案,则 f[i] 可从 f[i - 1] 转移而来。

对于 f[i - 1] 中的一个合法序列,其中共有 2(i - 1) 个元素,因此现需要将

插入其中,且满足

之前。

对于该问题,我们可以使用「插板法」的思想,即在 2(i - 1) 的基础上再加两个元素,即 2i。然后再在 2i 个元素中选取 2 个元素,前面的放

,后面的放

,因此共

种方案,即:

根据上述式子,递推即可得到答案。

另外我们还可以从「组合计数」的角度直接计算出 f[n] 的值。我们可以这样思考,若不要求

之前,则 n 笔订单共

种方案。

另外,f[n] 表示每个

都在

之前的方案数,因此我们考虑如何从 f[n] 推出

不难发现,对于 f[n] 中的每个序列,其中的

均可选择交换或者不交换来形成

中的一个序列,因此根据乘法原理,可以得到

其中

代码实现

class Solution {public: int countOrders(int n) { long long mod = 1e9 7, ans = 1; for (int i = 1; i <= n; i ) { ans = ans * i % mod; ans = ans * (2 * i - 1) % mod; } return ans; }};


60. 排列序列题目描述

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123""132""213""231""312""321"

给定 n 和 k,返回第 k 个排列。


示例 1

输入:n = 3, k = 3输出:"213"示例 2

输入:n = 4, k = 9输出:"2314"示例 3

输入:n = 3, k = 1输出:"123"提示解题思路

该题给定排列的排名,返回原排列,是「康托展开」的逆问题。因此我们可以根据「康托展开」的思想,来反推「逆康托展开」的实现过程。

依然以 [2,5,3,4,1] 为例来进行算法阐述。根据前文可知,该排列的排名为 46,首先让 46 - 1 = 45,即有 45 个排列比当前排列小。

接下来计算排列的第一位数字,

即有 1 个数小于当前排列的第一位,因此第一位为 2,更新

计算第二位的数字,

即有 3 个数小于当前排列的第二位,除去第一位的 2,可知第二位为 5,更新

以此类推,可以得知原排列为 [2,5,3,4,1]。根据上述过程的思想,便可实现本题的「逆康托展开」。

代码实现

class Solution {public: string getPermutation(int n, int k) { vector fac(n 1, 0), vis(n 1, 0); string ans; fac[0] = 1, k--; for (int i = 1; i <= n; i ) fac[i] = fac[i-1] * i; for (int i = n; i >= 1; i--) { int now = k / fac[i-1]; k -= fac[i-1] * now; for (int j = 1; j <= n; j ) { if (now == 0 && !vis[j]) { ans = '0' j; vis[j] = 1; break; } if (!vis[j]) now--; } } return ans; }};


总结

本文的总体框架如下,重点在于对上文中各个「组合计数」方法的掌握与理解。

文中算法大多为「组合计数」的基础算法,大家日后刷题遇到时可以及时巩固相关知识点,祝大家刷题愉快!


BY /

本文作者:Gene_Liu

声明:本文归 “力扣” 版权所有,如需转载请联系。文章封面图和文中部分图片来源于网络,如有侵权联系删除。

分享:

qq好友分享 QQ空间分享 新浪微博分享 微信分享 更多分享方式

热招院校推荐

更多院校>
(c)2024 www.wyfx2014.com All Rights Reserved SiteMap 联系我们 | 浙ICP备2023018783号