排列与组合问题:映射方法

  在排列与组合问题中,有这样的一种方法:将复杂问题的空间映射到一个容易求解的问题的空间上,这个映射可以是1-1映射,或者是1对m,m对1的(根据除法原理,要求m是一个常数)。

n元素的循环r排列

  每一个循环r排列对应r个不同的线性r排列。由除法原理,n元素集合的循环r排列的数目为 P ( n , r ) r = n ! r ( n − r ) ! \frac{P(n,r)}{r} =\frac{n!}{r(n-r)!} rP(n,r)=r(nr)!n!
严谨地,还要说明,每一个线性r排列,都能唯一的对应于一个循环r排列。

多重集合,有限重复数的排列问题

  设S是多重集合,它有k种不同类型的对象,且每一种类型的有限重复数分别是n1,n2,…,nk。设S的大小为n=n1+n2+…+nk。则根据乘法原理,S的排列数目为:
( n n 1 ) ( n − n 1 n 2 ) ( n − n 1 − n 2 n 3 ) . . . ( n − n 1 − n 2 − . . . − n k − 1 n k ) = n ! n 1 ! n 2 ! . . . n k ! \begin{pmatrix}n \\n_1 \end{pmatrix}\begin{pmatrix}n-n_1 \\n_2 \end{pmatrix} \begin{pmatrix}n-n_1-n_2 \\n_3 \end{pmatrix}...\begin{pmatrix}n-n_1-n_2-...-n_{k-1} \\n_k \end{pmatrix}=\frac{n!}{n_1!n_2!...n_k!} (nn1)(nn1n2)(nn1n2n3)...(nn1n2...nk1nk)=n1!n2!...nk!n!
  推广:设n是正整数,并设n1,n2,…,nk是正整数且n=n1+n2+…+nk。把n对象集合划分成k个标有标签的盒子,且第1个盒子含有n1个对象,第2个盒子含有n2个对象,…,第k个盒子含有nk个对象,这样的划分方法数等于 n ! n 1 ! n 2 ! . . . n k ! \frac{n!}{n_1!n_2!...n_k!} n1!n2!...nk!n!。原因是将n个对象放入k个盒子的划分方法数,可以使用乘法原理,因此得到了和上面相同的结果。
  可以看出,多重集合A的有限重复数的n排列的问题,等价于n个不同对象的集合B按照A中各元素的重复数进行带标签的划分的问题。事实上,它们构成了一个1-1映射:B对象的下标可以对应A的一个排列的一个位置,从而确定了这个位置在A集合中的类型属性。
  如果这些盒子没有标签,且有n1=n2=…=n k,划分数会有什么变化呢?这两个条件事实上让k个盒子没有了任何区别。因此,虽然我们暂时不知道n个元素的划分方法有多少种,但是我们可以发现,对于任意一种划分方法,可以通过k!种不同的贴标签方式,从而对应于唯一一种之前不同标签下的划分方法。这样就建立起了一个1对k!的映射,根据除法原理,划分数为 n ! k ! n 1 ! n 2 ! . . . n k ! \frac{n!}{k!n_1!n_2!...n_k!} k!n1!n2!...nk!n!

最多m个A和最多n个B的排列数

  构造多重集S={(m+1)·A,(n+1)·B},S是S的全排列中减去一个排列(A…AB…B)的排列的集合。
  对S中的每一个元素,按顺序做以下两步操作:(1)删去从左往右数第一个B,以及这个B左边的所有A;(2)删去从右往左数第一个A,以及这个A右边的所有B。操作后的元素,恰好就是一个最多m个A,最多n个B的排列。
  下面证明:每个最多m个A,最多n个B的排列都能被上述操作得到。
  假设排列X=x1x2…xk不能被上述操作得到,设X中A的个数为SA,B的个数为SB,有SA<m+1,SB<n+1,至少还能添加1个A和1个B,保证A和B的数量都不会超出S中排列的A,B的数量。因此可以构造排列X=A…ABx1x2…xkAB…B,左边的连续A和右边的连续B按数量补齐,使得X中有m+1个A,n+1个B。显然X∈S,与假设矛盾。因此假设不成立,原命题成立。又因为两个集合元素都是有限的,因此不会出现有元素映射不到的情况,因此,最多m个A和最多n个B的排列和S中的元素构成一个1-1映射。排列数为 ∣ S ′ ∣ = ( m + n + 2 m + 1 ) − 1 \left | S' \right |=\begin{pmatrix}m+n+2\\m+1\end{pmatrix} -1 S=(m+n+2m+1)1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个很有趣的问题。这里我提供一个用回溯法解决此问题的C语言实现。 首先,我们需要一个映射表,将数字和字母对应起来。可以使用一个二维数组来实现,如下所示: ```c char *map[10] = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; ``` 然后,我们可以用回溯法来生成所有可能的字母组合。具体来说,我们可以从字符串的第一个数字开始,逐个枚举它所对应的所有字母,然后递归地处理剩下的数字,直到处理完所有数字为止。在递归过程中,我们需要维护一个当前的字母组合,以及它对应的字符串下标。当处理到最后一个数字时,我们将当前字母组合加入结果集中。 以下是完整的代码实现: ```c #define MAX_N 100 char *map[10] = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; char res[MAX_N][MAX_N]; int res_size; void backtrack(char *digits, int idx, char *cur) { if (digits[idx] == '\0') { strcpy(res[res_size++], cur); return; } int num = digits[idx] - '0'; char *letters = map[num]; for (int i = 0; letters[i]; i++) { cur[idx] = letters[i]; backtrack(digits, idx + 1, cur); } } char **letterCombinations(char *digits, int *returnSize) { res_size = 0; int n = strlen(digits); if (n == 0) { *returnSize = 0; return NULL; } char *cur = (char *)malloc(n + 1); memset(cur, 0, n + 1); backtrack(digits, 0, cur); *returnSize = res_size; return (char **)res; } ``` 以上代码中,`backtrack` 函数是核心递归函数,`letterCombinations` 函数是入口函数。 注意,为了方便,我们将结果集存储到一个全局数组 `res` 中,所以在使用时需要注意多线程安全问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值