排列与组合(permutation combination)
一、排列的定义及公式:
从N个不同的元素中,任取M(M与N均为自然数,下同)个元素按照一定的顺序排成一列,叫做从N个不同元素中取出M个元素的一个排列;
从N个不同的元素中取出M个元素的所有排列的个数,叫做从N个不同元素中取出M个元素的一个排列数;公式为:
二、组合的定义及公式
从N个不同的元素中,任取M(M与N均为自然数,下同)个元素组成一组,叫做从N个不同元素中取出M个元素的一个组合;
从N个不同的元素中取出M个元素的所有组合的个数,叫做从N个不同元素中取出M个元素的一个组合数;公式为:
三、圆排列
线性排列时,n 个不同元素可以排列成 n! 种方式。
但是在圆形排列中,旋转视为相同的排列(即:从哪个元素开始走一圈是一样的)。
举个例子:
假设我们有 4 个人 A, B, C, D,要围成一圈坐下。
所有线性排列有:
A B C DA B D CA C B DA C D B ...
一共有 4! 种线性排列。
但围成一圈坐时,下面这些都被认为是同一种圆排列(因为只是起点不同,顺序没变):
A B C DB C D AC D A BD A B C
它们是同一个环,只是旋转了一下。因此:每种圆排列对应 n种线性排列(起点不同)。
所以数学公式是:
圆排列数= \(\frac{n!}{n} \)= (n – 1)!
不区分顺时针与逆时针
- 也就是说:只要绕圈顺序相同,不管是顺时针还是逆时针,都算同一种。
- 例如 A-B-C-D 和 A-D-C-B 被认为是同一种排列。
- 那么,每个圆排列会被重复计算 两次(一个顺时针,一个逆时针)。
- 因此公式变为: \(\frac{(n-1)!}{2}\),
三、计算排列及组合的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /**************************************************************** * C++ 计算排列数和组合数 * date:20222-5-3 * version: 1.0 * author: Alex Li ****************************************************************/ #include <iostream> using namespace std; int fact(int n) { if (n == 0 || n == 1) return 1; else return n * fact(n - 1); } int main() { int n, m, comb, per; cout<<"Enter N : "; cin>>n; cout<<"\nEnter M : "; cin>>m; comb = fact(n) / (fact(m) * fact(n-m)); cout << "\nCombination : " << comb; per = fact(n) / fact(n-m); cout << "\nPermutation : " << per; return 0; } |
四、给定字符串,输出全排列:
代码实现:方法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2023-01-20 16:45:45 * 最后修改: 2026-02-20 21:33:06 * 文件描述: 全排列 原地交换法 ****************************************************************/ #include <bits/stdc++.h> using namespace std; int n; int sum; int a[20]; void dfs(int k){ if(k == n){ sum++; for(int i=0;i<n;i++) //输出排列 cout << a[i]; cout << "\n"; return; } for(int i=k;i<n;i++){ swap(a[k], a[i]); dfs(k+1); swap(a[k], a[i]); // 回溯 } } int main(){ cin >> n; for(int i=0;i<n;i++) cin>>a[i]; dfs(0); cout<<"sum="<<sum; //输出全排列数 } |
方法二:DFS+回溯+used
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2026-02-20 21:47:09 * 最后修改: 2026-02-20 21:52:42 * 文件描述: 全排列 回溯+used ****************************************************************/ #include <bits/stdc++.h> using namespace std; int n; int sum; int a[20]; // 输入的原数组 int p[20]; // 当前排列 bool used[20]; // 标记某个位置是否被用过 void dfs(int k){ if(k == n){ // 注意这里改成 == n sum++; for(int i=0;i<n;i++) cout << p[i] << " "; cout << "\n"; return; } for(int i=0;i<n;i++){ if(!used[i]){ used[i] = true; p[k] = a[i]; // 用输入数组 dfs(k+1); used[i] = false; } } } int main(){ cin >> n; for(int i=0;i<n;i++) cin >> a[i]; dfs(0); cout<<sum; } |
五、取m个元素排列:
只要把递归的停止条件改成k=m即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2023-07-07 14:15:34 * 最后修改: 2026-02-20 21:58:10 * 文件描述: m个元素的排列 ****************************************************************/ #include <bits/stdc++.h> using namespace std; int n, m,sum; int a[20]; void dfs(int k){ if(k == m){ //改成k=m sum++; for(int i=0;i<m;i++) cout << a[i]; cout << "\n"; return; } for(int i=k;i<n;i++){ swap(a[k], a[i]); dfs(k+1); swap(a[k], a[i]); } } int main(){ cin >> n >> m; for(int i=0;i<n;i++) a[i] = i+1; dfs(0); cout<<sum; } |
七、给定n个元素,从中取m个元素,输出组合
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2022-09-01 23:38:31 * 最后修改: 2026-02-20 22:51:41 * 文件描述: 组合数+剪枝 ****************************************************************/ #include <bits/stdc++.h> using namespace std; int n, m,sum; int a[20]; int chosen[20]; void dfs(int start, int k){ if(k == m){ // 选够 m 个 sum++; for(int i=0;i<m;i++) cout << chosen[i] << " "; cout << "\n"; return; } if(n - start < m - k) return; // 剩下的元素不够选了,提前剪枝 for(int i=start;i<n;i++){ // 从 start 开始 chosen[k] = a[i]; dfs(i+1, k+1); // 不能回头 } } int main(){ cin >> n >> m; for(int i=0;i<n;i++) cin >> a[i]; dfs(0, 0); cout<<sum; } |
洛谷:P1157
Quizzes
