关于排列与组合的枚举
最近刷题的时候碰到了这两种题型,主要就是计算排列数和组合数,自己做了之后看了一些解题发现帮助挺大的,做一个小笔记~
1.组合的枚举
组合就是从 n 个元素中抽出 r 个元素(不分顺序且 r ≤ n ),我们可以简单地将n个元素理解为自然数1, 2, …, n,从中任取 r 个数。
举个栗子:例如 n = 5, r = 3,则所有的组合为:123, 124, 125, 145, 234, 235, 245, 345
解法一:这题最容易想到的就是dfs,递归求解,搜索与回溯,比较简单,直接上代码:
1 | // 洛谷P1157 组合的输出 |
解法二:上面的递归解法虽然容易想到,但是存在一定的问题,就是当 r 比较大时, 递归的深度就会非常深,有可能就会导致程序无法运行,所以非递归解法就十分有必要了
主要存在四个分支(以输入为 m = 5, r = 3 为例):
- 已经填满 r 个:直接输出结果,例如 index = 4, num = [1, 2, 3]
- 当未填满且当前位为 0 时:将当前为赋值为前一位加一,例如 index = 3 , num = [1, 2, 0] -> [1, 2, 3]
- 当未填满且当前位不为0时:判断当前位是否能过加一(不超出 n 的范围,例如: index = 3 , num = [1, 2, 3] -> [1, 2, 4]
- 如果都不满足上述情况就向前回溯一位,并将当前位置为0,例如:index = 3, num = [1, 2, 5] -> index = 2, num= [1, 2, 0]
1 | // 洛谷P1157 组合的输出 |
2.排序的枚举
输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
举个栗子:例如 n = 3,则所有的组合为:123, 132, 213, 231, 312, 321
说在前面,可能C/C++选手会说直接next_permutation
就可以了,但是这里不考虑这种方法了,重在开拓思路!
解法一:同样的思路,也可以使用dfs深度搜索枚举出所有的情况,思路也非常的简单,代码易懂:
1 | // 洛谷P1706 全排列问题 |
解法二:同样的,上述递归解法也是存在如果n过大,递归层数就会过深,可能引起程序奔溃,比如上述提到的洛谷P1088 火星人,n 甚至回达到 10000 这个数量级,然后上述递归解法就崩溃了,所以就需要非递归解法。
非递归解法其实也非常的自然,就是模拟我们计算下一个排序数时的思路,我大概举了一个小栗子,需要可以参考一下:
然后就是代码实现了,其实就是严格按照上面思路些代码就好了~
1 |
|